前提・実現したいこと
Pythonにおいて、PILで画像処理を行なっています。
画像全体をピクセルごとに、予め用意したカラーパレットの色に変換したいと考えています。
ソースコードにある通り、(r,g,b)の順に並べたpaletteというタプルのリストを作り、画像内で1ピクセルごとにどの色を示すタプルが最も近い色なのかを判定しようとしています。
以下のソースコードではbase.jpgというファイルを処理してoutput.jpgを作ろうとしています。
私はpython初心者のため、ソースコードに汚い部分がると思います。
よろしくお願い致します。
該当のソースコード
Python
1from PIL import Image 2import numpy as np 3 4 5im=Image.open("base.jpg") #元画像base.jpg 6new_im=Image.new('RGB',(im.size[0],im.size[1]), (255,255,255)) 7#元画像と同じ大きさ、背景白の新たな画像 8 9 10def rgb_to_xyz(rgb): #rgb(255まで)→xyzの割合(0<=return<=1)を出す 11 a=[0.4124,0.3575,0.1805] 12 b=[0.2127,0.7152,0.0722] 13 c=[0.0193,0.1192,0.9504] 14 k=np.array([a,b,c]) #変換用倍率の行列 15 16 t=k.dot(np.array([x/255 for x in rgb])) 17 return (t[0]/0.9504,t[1]/1.0001,t[2]/1.0889) 18 #↑白(255,255,255)で(0.9504,1.0001,1.0889)なので割合として返す 19 20def F(t): #L*a*b*用の関数 21 if t>216/24389: 22 return 116*pow(t,1/3)-16 23 else: 24 return 24389/27*t 25 26def Lab(x,y,z): #xyz方式をL*a*b*に変換 27 L=F(y) 28 a=125/29*(F(x)-F(y)) 29 b=50/29*(F(y)-F(z)) 30 return (L,a,b) 31 32def dis(a,b): #ユークリッド距離(2乗での比較) 33 return sum([pow(Lab(*rgb_to_xyz(a))[x]-Lab(*rgb_to_xyz(b))[x],2) for x in range(2)]) 34 35 36palette=[(60, 60, 60), (120, 120, 120), (130, 130, 130), (90, 90, 90), (255, 255, 255), (130, 135, 145), (70, 175, 170), (190, 100, 130), (140, 60, 170), (100, 50, 40), (130, 130, 200), (40, 60, 40), (60, 100, 120), (80, 120, 170), (120, 40, 40), (90, 0, 0), (200, 0, 0), (170, 100, 40), (200, 185, 130), (120, 85, 60), (100, 70, 40), (80, 60, 40), (100, 140, 45), (115, 150, 70), (0, 170, 45), (100, 160, 20), (180, 180, 40), (200, 190, 60), (80, 100, 40), (0, 100, 0), (50, 70, 30), (40, 65, 40)] 37#↑用意した色のテンプレート(rgb) 38 39 40for x in range(im.size[0]): 41 for z in range(im.size[1]): 42 rgb=im.getpixel((x,z))[0:3] 43 m=[dis(rgb,p) for p in palette] 44 #↑現在指定しているピクセルの色(rgb)とパレットそれぞれを比較したリスト作成 45 new_im.putpixel((x,z),palette[m.index(min(m))]) #作成したリストの最小値に値するタプルで色付け 46 47 48new_im.save("output.jpg",quarity=90) 49
2つ目のソースコード
Python
1from PIL import Image 2import numpy as np 3 4 5im=Image.open("base.jpg") #元画像base.jpg 6new_im=Image.new('RGB',(im.size[0],im.size[1]), (255,255,255)) 7#元画像と同じ大きさ、背景白の新たな画像 8 9 10def rgb_to_xyz(rgb): #rgb(255まで)→xyzの割合(0<=return<=1)を出す 11 a=[0.4124,0.3575,0.1805] 12 b=[0.2127,0.7152,0.0722] 13 c=[0.0193,0.1192,0.9504] 14 k=np.array([a,b,c]) #変換用倍率の行列 15 16 t=k.dot(np.array([x/255 for x in rgb])) 17 return (t[0]/0.9504,t[1]/1.0001,t[2]/1.0889) 18 #↑白(255,255,255)で(0.9504,1.0001,1.0889)なので割合として返す 19 20def F(t): #L*a*b*用の関数 21 if t>216/24389: 22 return 116*pow(t,1/3)-16 23 else: 24 return 24389/27*t 25 26def Lab(x,y,z): #xyz方式をL*a*b*に変換 27 L=F(y) 28 a=125/29*(F(x)-F(y)) 29 b=50/29*(F(y)-F(z)) 30 return (L,a,b) 31 32def cos(a,b): 33 return sum([change(*a)[x]*change(*b)[x] for x in range(2)]) 34 35def C94(L1,a1,b1,L2,a2,b2): 36 k1=0.045 37 k2=0.015 38 kL=1 39 C1=(a1**2+b1**2)**(0.5) 40 C2=(a2**2+b2**2)**(0.5) 41 dC=C1-C2 42 dH=((a1-a2)**2+(b1-b2)**2-dC**2)**(0.5) 43 Sc=1+k1*C1 44 Sh=1+k2*C2 45 return (((L1-L2)/kL)**2+(dC/Sc)**2+(dH/Sh)**2)**(0.5) 46 47 48def dis(a,b): 49 return sum([C94(*Lab(*rgb_to_xyz(a)),*Lab(*rgb_to_xyz(b))) for x in range(2)]) 50 51 52palette=[(60, 60, 60), (120, 120, 120), (130, 130, 130), (90, 90, 90), (255, 255, 255), (130, 135, 145), (70, 175, 170), (190, 100, 130), (140, 60, 170), (100, 50, 40), (130, 130, 200), (40, 60, 40), (60, 100, 120), (80, 120, 170), (120, 40, 40), (90, 0, 0), (200, 0, 0), (170, 100, 40), (200, 185, 130), (120, 85, 60), (100, 70, 40), (80, 60, 40), (100, 140, 45), (115, 150, 70), (0, 170, 45), (100, 160, 20), (180, 180, 40), (200, 190, 60), (80, 100, 40), (0, 100, 0), (50, 70, 30), (40, 65, 40)] 53#↑用意した色のテンプレート(rgb) 54 55 56 57f![イメージ説明](9792dc25b0893bd07159f748839d1633.png)n range(im.size[0]): 58 for z in range(im.size[1]): 59 rgb=im.getpixel((x,z))[0:3] 60 m=[dis(rgb,p) for p in palette] 61 #↑現在指定しているピクセルの色(rgb)とパレットそれぞれを比較したリスト作成 62 new_im.putpixel((x,z),palette[m.index(min(m))]) #作成したリストの最小値に値するタプルで色付け 63 64 65new_im.save("output.jpg",quarity=90) 66 67 68
試したこと
近い色を判定するために、まずrgbのタプル同士でのユークリッド距離として近い色を検出させました。
次に、rgb配色のままだとあまり同系色を検出できないことを理解して、rgbをhsv形式に変換して同様にユークリッド距離で判定しました。
ユークリッド距離での計算が思うようにいかなかったため、コサイン類似度(空間ベクトル内での2ベクトルの内積の最小値)によって判定しました。
あまりいい結果が得られないため、次にrgbをxyz方式にして同様にユークリッド距離、コサイン類似度で計算しました。
最後に、現在のソースコードのようにrgb→xyz→Lab*方式に変換してユークリッド距離での判定をしました。
これらのことを行なったのですが似たような色へと変換することが困難でした。
《追記分》
ユークリッド距離での判定がイマイチだったので、2つ目のソースコードでは以下のURLの「CIE94」を試しています。
https://ja.m.wikipedia.org/wiki/色差
ユークリッド距離はそもそもの色が若干違う点、CIE94は大まかには合っているものの黒い斑点が多く入ってしまうこと、が問題点です。
回答1件
あなたの回答
tips
プレビュー