‘ソフトウェア技術’ カテゴリーのアーカイブ

[python]イメージから5色パレット出力スクリプト書いた

[python]イメージから5色パレット出力スクリプト書いたをはてなブックマークに追加 [python]イメージから5色パレット出力スクリプト書いたをdel.icio.usに追加 Yahoo!ブックマークに登録 [python]イメージから5色パレット出力スクリプト書いたをGoogle Bookmarksに追加 [python]イメージから5色パレット出力スクリプト書いたをtwitterにポスト
2010年6月1日 火曜日

kulerにあるカラーパレットがカッコ良かったのでiphoneのアプリで使いたいと思ったのですが、商用はダメみたいなのでパレットをジェネレートするpythonスクリプト書いてみました。

palette

どれも意外といい感じの配色になった。

palette2

仕組みはイメージファイルをロードしてヒストグラムをクラスタリングして5色のパレットをジェネレートすると言うもの。

DSCF0053

こんな感じの写真から

0000

これと

0001

これと

0002

こんなパレットを生成することができた。スクリプトが置いてあるディレクトリのjpgファイルを読み込んで3枚ずつ生成していくので沢山のパレットを作ることが出来ます。ざっと動かしてみた感じ、満足できるくらいの出力が得られました。

py2exeでwindows実行形式のファイルGenPalette.zip(解凍してください)も作っておきました。

コードはこちら。使うときはリネームしてください

処理の流れは以下のようになっています。
・イメージをロード
・ヒストグラムを取得。ヒストグラムというのは色情報の分布を表したもののようです。
・ヒストグラムをウォード法で5つのクラスタに分ける。
・各クラスタ中、一番嵩みの多い色情報を選んでパレット化
・各クラスタ中、一番嵩みの少ない色情報を選んでパレット化
・各クラスタ中、ランダムに色情報を選んでパレット化

パレット数は5個でコーディングしてるけど、paletteSizeを変えれば増やしたり減らしたり出来ます。
palette.txtには以下のような出力がされます。
処理が結構重いのですがクラスタリングの方法を変えれば軽くなるかもしれません。

[(223, 184, 181), (122, 86, 104), (120, 72, 85), (99, 50, 56), (85, 36, 39)] // max value in histogram of clusters [ DSCF0053.JPG to 0000.png ]
[(240, 212, 216), (221, 189, 196), (193, 147, 160), (149, 121, 120), (141, 97, 92)] // min value in histogram of clusters [ DSCF0053.JPG to 0001.png ]
[(223, 209, 201), (178, 124, 159), (109, 53, 107), (107, 87, 169), (100, 81, 181)] // random per clusters [ DSCF0053.JPG to 0002.png ]
# coding: utf-8

import random
import re
import sys, os, glob
from PIL import Image, ImageFont, ImageDraw

class elem:
	def __init__( self, grpCnt, dataCnt, rngCnt, grpRng, data ):
		self.grpCnt = grpCnt
		self.dataCnt = dataCnt
		self.rngCnt = rngCnt
		self.grpRng = grpRng
		self.data = data

def analysis( store ):

	dataCntBuf = [ 1 ] * store.dataCnt
	rangeBuf = [ [ 0 ] * store.dataCnt for idx in range( store.dataCnt ) ]

	for idx in range( store.dataCnt ):
		store.grpRng[ idx ] = idx
		ndx = idx + 1
		while ndx < store.dataCnt:
			rangeBuf[ idx ][ ndx ] = getRange( store, idx, ndx )
			ndx += 1

	for idx in range( store.dataCnt ):
		ndx = idx + 1
		while ndx < store.dataCnt:
			rangeBuf[ ndx ][ idx ] = rangeBuf[ idx ][ ndx ]
			ndx += 1

	loopCnt = store.dataCnt
	while loopCnt > store.grpCnt:
		curRange = getMinimumRange( store, rangeBuf )
		for idx in range( store.dataCnt ):
			if store.grpRng[ idx ] == idx :
				ndx = idx + 1
				while ndx < store.dataCnt:
					if store.grpRng[ ndx ] == ndx :
						if idx != curRange[ "to" ] and ndx != curRange[ "to" ] :
							if idx == curRange[ "from" ] :
								rangeBuf[ idx ][ ndx ] = analyticRange({
									"dest1" : rangeBuf[ curRange[ "from" ] ][ ndx ],
									"dest2" : rangeBuf[ curRange[ "to" ] ][ ndx ],
									"dest3" : rangeBuf[ curRange[ "from" ] ][ curRange[ "to" ] ],
									"size1" : dataCntBuf[ curRange[ "from" ] ],
									"size2" : dataCntBuf[ curRange[ "to" ] ],
									"size3" : dataCntBuf[ ndx ]
								})
								rangeBuf[ ndx ][ idx ] = rangeBuf[ idx ][ ndx ]
							elif ndx == curRange[ "to" ] :
								rangeBuf[ idx ][ ndx ] = analyticRange({
									"dest1" : rangeBuf[ idx ][ curRange[ "from" ] ],
									"dest2" : rangeBuf[ idx ][ curRange[ "to" ] ],
									"dest3" : rangeBuf[curRange[ "from" ] ][ curRange[ "to" ] ],
									"size1" : dataCntBuf[ curRange[ "from" ] ],
									"size2" : dataCntBuf[ curRange[ "to" ] ],
									"size3" : dataCntBuf[ ndx ]
								});
								rangeBuf[ ndx ][ idx ] = rangeBuf[ idx ][ ndx ]
					ndx += 1
		dataCntBuf[ curRange[ "from" ] ] += dataCntBuf[ curRange[ "to" ] ]
		for idx in range( store.dataCnt ):
			if store.grpRng[ idx ] == curRange[ "to" ] :
				store.grpRng[ idx ] = curRange[ "from" ]
		loopCnt -= 1

	grpidx = 1
	retCluster = []
	while( grpidx <= store.grpCnt ):
		curGrp = []
		grpndx = -1;
		idx = 0
		while ( idx < store.dataCnt and grpndx < 0 ) :
			if store.grpRng[ idx ] >= 0 :
				grpndx = store.grpRng[ idx ]
			idx += 1

		idx = 0
		while ( idx < store.dataCnt ) :
			if store.grpRng[ idx ] == grpndx :
				ndx = 0
				while ( ndx < store.rngCnt ):
					curGrp.append( ( store.data[ idx ][ ndx ], idx ) )
					ndx += 1
				store.grpRng[ idx ] = -1;
			idx += 1
		grpidx+=1
		retCluster.append( curGrp )

	return retCluster

def getRange( store, _from, _to ):
	buf = 0
	retRange = 0
	for idx in range( store.rngCnt ):
		buf = store.data[ _from ][ idx ] - store.data[ _to ][ idx ]
		retRange = retRange + ( buf * buf )
	return retRange

def getMinimumRange( store, rangeBuf ) :
	curRange = { "from" : 0, "to" : 0 }
	min = -1.0
	for idx in range( store.dataCnt ):
		if store.grpRng[ idx ] == idx :
			ndx = idx + 1
			while ndx < store.dataCnt:
				if store.grpRng[ ndx ] == ndx :
					if min < 0.0 or rangeBuf[ idx ][ ndx ] < min :
						min = rangeBuf[ idx ][ ndx ]
						curRange[ "from" ] = idx
						curRange[ "to" ] = ndx
				ndx += 1

	return curRange

def analyticRange( relation ) :
	range = (
		( relation[ "size1" ] + relation[ "size3" ] ) * relation[ "dest1" ] +
		( relation[ "size2" ] + relation[ "size3" ] ) * relation[ "dest2" ] -
		relation[ "size3" ] * relation[ "dest3" ]
	) / ( relation[ "size1" ] + relation[ "size2" ] + relation[ "size3" ] )
	return range;

def main():
	paletteSize = 5
	paletteFile = open('palette.txt', 'w')
	paletteFileIdx = 0
	fileList = glob.glob('*.*')
	for imgFile in fileList:
		if re.compile( "\.jpg$|\.jpeg$", re.IGNORECASE ).search( imgFile ) == None :
			continue
		print imgFile
		image = Image.open( imgFile )
		result = image.convert( 'P', palette=Image.ADAPTIVE, colors=255 )
		palette = result.getpalette()
		hstgrm = [ [ idx ] for idx in result.histogram() ]
		count = len( result.histogram() ) -1
		dataStore = elem( paletteSize, count, 1, [ 0 ] * count, hstgrm )
		clst = analysis( dataStore )
		maxColorPalette = []
		minColorPalette = []
		rndColorPalette = []

		for idx in range( paletteSize ):
			maxColorPalette.append(
				(
					palette[ ( max(clst[ idx ])[ 1 ] * 3 ) ],
					palette[ ( max(clst[ idx ])[ 1 ] * 3 ) + 1 ],
					palette[ ( max(clst[ idx ])[ 1 ] * 3 ) + 2 ]
				)
			)
			minColorPalette.append(
				(
					palette[ ( min(clst[ idx ])[ 1 ] * 3 ) ],
					palette[ ( min(clst[ idx ])[ 1 ] * 3 ) + 1 ],
					palette[ ( min(clst[ idx ])[ 1 ] * 3 ) + 2 ]
				)
			)
			rndColorPalette.append(
				(
					palette[ ( clst[ idx ][int( random.random() * len( clst[ idx ] ) )][ 1 ] * 3 ) ],
					palette[ ( clst[ idx ][int( random.random() * len( clst[ idx ] ) )][ 1 ] * 3 ) + 1 ],
					palette[ ( clst[ idx ][int( random.random() * len( clst[ idx ] ) )][ 1 ] * 3 ) + 2 ]
				)
			)
		ctgry = [ "max value in histogram of clusters", "min value in histogram of clusters", "random per clusters"]
		for palette in [ maxColorPalette, minColorPalette, rndColorPalette ]:
			palette.sort()
			palette.reverse()
			paletteFile.write( str( palette ) + " // " + ctgry[ paletteFileIdx%3 ] + " [ " + imgFile + " to " + '%(#)04d'%{'#':paletteFileIdx} + ".png ]\n" )
			paletteImg = Image.new( "RGB", ( paletteSize * 20, int( paletteSize * 20 * 5 / 8 ) ), ( 0xff, 0xff, 0xff ) )
			draw = ImageDraw.Draw( paletteImg )
			for idx in range( paletteSize ):
				draw.rectangle(
					( idx * 20, 0, idx * 20 + 20, int( paletteSize * 20 * 5 / 8 ) ),
					outline = (
						palette[ idx ][ 0 ],
						palette[ idx ][ 1 ],
						palette[ idx ][ 2 ]
					),
					fill = (
						palette[ idx ][ 0 ],
						palette[ idx ][ 1 ],
						palette[ idx ][ 2 ]
					)
				)
			paletteImg.save( '%(#)04d'%{'#':paletteFileIdx} + ".png", "png" )
			paletteFileIdx += 1
	paletteFile.close()

if __name__ == '__main__':
	main()

[iPhone]aniQuoma申請しました

[iPhone]aniQuoma申請しましたをはてなブックマークに追加 [iPhone]aniQuoma申請しましたをdel.icio.usに追加 Yahoo!ブックマークに登録 [iPhone]aniQuoma申請しましたをGoogle Bookmarksに追加 [iPhone]aniQuoma申請しましたをtwitterにポスト
2010年5月23日 日曜日

aniQuoma(アニコマ)をappleに申請しました。AA Clipa!の時は承認まで5日くらいかかったので今回もそのくらいかかるのだろうか。

IMG_0020

アイコンはこんな感じ。

IMG_0017

スプラッシュはこんな感じ。

アプリの内容は前回のポストで書いてみました。公開されたらまたお知らせします。

[iPhone]写真加工アプリ実装中です

[iPhone]写真加工アプリ実装中ですをはてなブックマークに追加 [iPhone]写真加工アプリ実装中ですをdel.icio.usに追加 Yahoo!ブックマークに登録 [iPhone]写真加工アプリ実装中ですをGoogle Bookmarksに追加 [iPhone]写真加工アプリ実装中ですをtwitterにポスト
2010年5月13日 木曜日

5月の末あたりにiphoneアプリをリリースする予定でして、アプリ部分の実装は大体できてきたのでどんなことをやろうとしているのか公開してみます。

aniQuoma!(アニコマ!)というアプリ名で、写真を加工して効果をつけてあげるアプリです。

IMG_0657

コンビニのドリンク陳列棚に

IMG_0658

こんな効果をつけてなんかスタイリッシュにしてみたり

IMG_0733

ビナウォークに来ていた超新塾のみなさんの写真ですが

IMG_0754

こんな感じの効果でちょっと硬派じゃなくなってしまったり

IMG_0762

ソファに置いたリモコンが

IMG_0764

ものすごい勢いでシリアスな感じになったり

IMG_0744

こむぎの写真も

IMG_0745

なんか決めゴマっぽくなったりというアプリです。

ということで、115円で販売する予定です。AA Clipa!でやったようなtwitpicに送信とかメール送信とかそういう実装もされます。良かったら買ってくださいね!