0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Pyxel】短いプログラムで遊ぶ③

Last updated at Posted at 2024-11-29

PyxelとPILライブラリを使って、SlideShowを作成しました。
「16色を超える色」の255色に減色して表示を繰り返しています。

やってることは
1.画像ファイルを読み込み
2.RGBAモードに変換
3.画像サイズ変更(画像の縦横比を維持)
4.255色に減色
5.表示

前準備として、
内部で減色したカラーデータをmy_resource.pyxpalに保存して使用するため、本プログラムの実行前にPyxelエディタを実行("pyxel edit")、ファイル保存して空のリソースファイル:my_resource.pyxresを作成してください。
また、画像ファイルをカレントのimgフォルダに格納してください。

表示画像サイズや読み込みフォルダを変更したいときはコード内の該当箇所を変更してください。表示できないファイルはコンソールにエラー内容を表示してとばします。

操作方法は、マウス左クリックで次の画像へ、右クリックで終了です。

from PIL import Image
import numpy as np
import pyxel
import os

#表示画像サイズ
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
#画像フォルダ指定
_folder_path = './img'
#----------------------------------------------------
#画像表示
def disp_image(filename):
	try:
		im = Image.open(filename)
	except ValueError as e:
		disp_error(filename + ": Image.open ERROR[ValueError]")
		print(e)
		return
	except OSError as e:
		disp_error(filename + ": Image.open ERROR[OSError]")
		print(e)
		return
	except PIL.UnidentifiedImageError as e:
		disp_error(filename + ": Image.open ERROR[PIL.UnidentifiedImageError]")
		print(e)
		return
	except:
		disp_error(filename + ": Image.open ERROR[Other Error]")
		return
#----------------------------------------------------
#RGBAモードに変換
	try:
		im = im.convert('RGB')
	except ValueError as e:
		disp_error(filename + ": convert RGB ERROR[ValueError]")
		print(e)
	except OSError as e:
		disp_error(filename + ": convert RGB ERROR[OSError]" )
		print(e)
	except:
		disp_error(filename + ": RGB convert ERROR")
		return
#----------------------------------------------------
#画像サイズ変更
	try:
		resize_im = im.copy()
		#画像の縦横比を維持したままリサイズ
		resize_im.thumbnail(( SCREEN_WIDTH, SCREEN_HEIGHT ))

		#縦横のサイズの大きい方が指定サイズに収まるように拡大(割合リサイズの必要あり)
		if( resize_im.width > resize_im.height ):
			if( SCREEN_WIDTH > resize_im.width ):
				resize_im = resize_im.resize(( SCREEN_WIDTH, SCREEN_HEIGHT ))

		#指定サイズの中央に画像を置く
		width, height = resize_im.size
		yohaku_w = int((SCREEN_WIDTH - width)/2)
		yohaku_h = int((SCREEN_HEIGHT - height)/2)
		result = Image.new(resize_im.mode, (SCREEN_WIDTH, SCREEN_HEIGHT), (0,0,0))
		result.paste(resize_im, (yohaku_w, yohaku_h))
		
		resize_im = result.copy()
	except ValueError as e:
		disp_error(filename + ": resize ERROR[ValueError]")
		print(e)
		return
	except OSError as e:
		disp_error(filename + ": resize ERROR[OSError]" )
		print(e)
		return
	except:
		disp_error(filename + ": resize ERROR[Other Error]")
		return
#----------------------------------------------------
#減色
	try:
		im_q = resize_im.quantize(colors=255, method=0, kmeans=100, dither=1)
	except ValueError as e:
		disp_error(filename + ": resize_im.quantize ERROR[ValueError]")
		peint(e)
		return
	except OSError as e:
		disp_error(filename + ": resize_im.quantize ERROR[OSError]")
		peint(e)
		return
	except:
		disp_error(filename + ": resize_im.quantize ERROR[Other Error]")
		return
#----------------------------------------------------
#画像からパレットを取得してRGBの順になるように3つずつのlistに格納する
	_palette = list(zip(*[iter(im_q.getpalette())]*3))
#----------------------------------------------------
#画像データ抽出して表示
	_pix = np.asarray(im_q)
	with open('./my_resource.pyxpal', mode='w') as f:
		for _col in range(len(_palette)):
			f.write( hex(_palette[_col][0]*0x10000 + _palette[_col][1]*0x100 + _palette[_col][2]).removeprefix('0x')+'\n' )
#255色カラーデータを読み込むためリソース読み込み
	pyxel.load("my_resource.pyxres")
#画面クリア
	pyxel.cls(0)
#描画
	_scrptr = pyxel.screen.data_ptr()
	for _yp in range(SCREEN_HEIGHT):
		_s = _yp * SCREEN_WIDTH
		_e = _s + SCREEN_WIDTH
		_scrptr[ _s : _e ] = _pix[_yp]
#キー入力待ち
#----------------------------------
def disp_error(_str):
	global _flag
	#エラー画像はとばす
	_flag = 2
	print(_str)
#----------------------------------------------------
#左クリックで次の画像へ、右クリックで終了
_flag = 0
_count = 0
_piclist =[]

def update():
	global _flag
	global _count
	global _piclist

	if( _flag == 1 ):
		if( pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT) ):
			_flag = 0
			_count = _count + 1
			if( _count >= len( _piclist ) ):
				_count = 0
		elif( pyxel.btnp(pyxel.MOUSE_BUTTON_RIGHT) ):
			pyxel.quit()
	#Error発生時はすぐに次へ
	elif( _flag == 2 ):
		_flag = 0
		_count = _count + 1
		if( _count >= len( _piclist ) ):
			_count = 0
#----------------------------------
def draw():
	global _flag
	global _count
	global _piclist

	if( _flag == 0 ):
		_flag = 1
		disp_image( _folder_path + '/' + _piclist[_count].rstrip() )
#----------------------------------------------------
pyxel.init( SCREEN_WIDTH, SCREEN_HEIGHT )

try:
	_piclist = os.listdir(_folder_path)
except FileNotFoundError:
	print("イメージフォルダ",_folder_path,"が無いので終了")
	pyxel.quit()
except:
	pyxel.quit()

#初期化
_flag = 0
_count = 0
#実行
pyxel.run(update, draw)

Pyxelではこのように外部ライブラリを使うこともできます。
ただ、html化した時にPILライブラリが無いと言われてしまうので今後に期待!

参考までに、Pillow対応画像形式(ただし必ず表示できるわけではありません)
(参考URL:https://pillow.readthedocs.io/en/latest/handbook/image-file-formats.html)

[Full Support]
BLP, BMP, DDS, DIB, EPS, GIF, ICNS, ICO, IM, JPEG, JPEG2000, MSP,
PCX, PFM, PNG, APNG, PPM, SGI, SPIDER, TGA, TIFF, WebP, XBM,
[Read-only]
CUR, DCX, FITS, FLI, FLC, FPX, FTEX, GBR, GD, IMT, IPTC/NAA,
MCIDAS, MIC, MPO, PCD, PIXAR, PSD, QOI, SUN, WAL, WMF, EMF, XPM
[Write-only]
PALM, PDF, XV Thumbnails
[Identify-only]
BUFR, GRIB, HDF5, MPEG

後記:
エラー対応や処理方法など不備があるかもしれません。
その時は指摘頂けると助かります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?