color_data = materials_list_from_file('average_color.csv')
icon_im = Image.open('my_icon.png')
display_image(icon_im)
icon_im_width, icon_im_height = icon_im.size
mosaic_icon_im = Image.new('RGBA', (1600, 1600))
for left in range(0, icon_im_width, DOT_AREA_ONE_SIDE):
for top in range(0, icon_im_height, DOT_AREA_ONE_SIDE):
average_color = calc.average_color_in_range(icon_im, left, top,
left+DOT_AREA_ONE_SIDE, top+DOT_AREA_ONE_SIDE)
if len(average_color) != 3:
continue
filename = similar_color_filename(average_color, color_data)
# 距離最小のファイルを縮小して1600×1600の画像に貼り付け
area_im = Image.open('image/euph_part_icon/'+filename)
area_im.thumbnail((THUMBNAIL_ONE_SIDE, THUMBNAIL_ONE_SIDE))
mosaic_icon_im.paste(area_im, (left//DOT_AREA_ONE_SIDE * THUMBNAIL_ONE_SIDE,
top//DOT_AREA_ONE_SIDE * THUMBNAIL_ONE_SIDE))
save_path = 'product/my_icon_mosaic_mean.png'
mosaic_icon_im.save(save_path)
# Display Image ##### @@@@@ #####
#画像の読み込み
im = Image.open(save_path)
display_image(im)
calculate_average_color.py
import os
import csv
from PIL import Image
from mosaic_art import calc
data_list = []
for image_name in os.listdir('image/euph_part_icon'):
if not image_name.endswith('.png'):
continue
im = Image.open('image/euph_part_icon/'+image_name)
im_width, im_height = im.size
red, green, blue = calc.average_color_in_range(im, 0, 0, im_width, im_height)
data_list.append([image_name, red, green, blue])
with open('average_color.csv', 'w', newline='') as csv_file:
csv_writer = csv.writer(csv_file)
csv_writer.writerows(data_list)
calculate_mode_color.py
import os
import csv
from PIL import Image
from mosaic_art import calc
data_list = []
for image_name in os.listdir('image/euph_part_icon'):
if not image_name.endswith('.png'):
continue
im = Image.open('image/euph_part_icon/'+image_name)
im_width, im_height = im.size
red, green, blue = calc.mode_color_in_range(im, 0, 0, im_width, im_height)
data_list.append([image_name, red, green, blue])
with open('mode_color.csv', 'w', newline='') as csv_file:
csv_writer = csv.writer(csv_file)
csv_writer.writerows(data_list)
差分は2箇所
$ diff calculate_average_color.py calculate_mode_color.py
15c15
< red, green, blue = calc.average_color_in_range(im, 0, 0, im_width, im_height)
---
> red, green, blue = calc.mode_color_in_range(im, 0, 0, im_width, im_height)
18c18
< with open('average_color.csv', 'w', newline='') as csv_file:
---
> with open('mode_color.csv', 'w', newline='') as csv_file:
→クラスのプロパティとして持たせる。
インスタンス作成時の引数で適切なプロパティを設定する
class ColorCalculator:
def __init__(self, calc_type):
self.calc_func = ColorCalculator.calculate_function(calc_type)
self.csv_name = ColorCalculator.color_csv_name(calc_type)
def calculate(self):
data_list = []
for image_name in os.listdir('image/euph_part_icon'):
if not image_name.endswith('.png'):
continue
im = Image.open('image/euph_part_icon/'+image_name)
im_width, im_height = im.size
red, green, blue = self.calc_func(im, 0, 0, im_width, im_height)
data_list.append([image_name, red, green, blue])
with open(self.csv_name, 'w', newline='') as csv_file:
csv_writer = csv.writer(csv_file)
csv_writer.writerows(data_list)
→[今後] 関数を使って実装して比較してみる
icon_im = Image.open('slack.png')
icon_im_width, icon_im_height = icon_im.size
icon_im
# 元の画像の平均色の可視化
dot_icon_im = icon_im.copy()
for left in range(0, icon_im_width, DOT_AREA_ONE_SIDE):
for top in range(0, icon_im_height, DOT_AREA_ONE_SIDE):
red, green, blue = calc.average_color_in_range(icon_im, left, top,
left+DOT_AREA_ONE_SIDE, top+DOT_AREA_ONE_SIDE)
average_color_im = Image.new('RGBA',
(DOT_AREA_ONE_SIDE, DOT_AREA_ONE_SIDE),
(red, green, blue, 255)) # a=0だと透明で何も見えない
dot_icon_im.paste(average_color_im, (left, top))
display_image(dot_icon_im)
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-13-a8bc5bee0fa7> in <module>() 4 for top in range(0, icon_im_height, DOT_AREA_ONE_SIDE): 5 red, green, blue = calc.average_color_in_range(icon_im, left, top, ----> 6 left+DOT_AREA_ONE_SIDE, top+DOT_AREA_ONE_SIDE) 7 average_color_im = Image.new('RGBA', 8 (DOT_AREA_ONE_SIDE, DOT_AREA_ONE_SIDE), ~/study/20171202mosaic-art-python/mosaic_art/calc.py in average_color_in_range(icon_im, left, top, right, bottom) 28 mean_color = ImageStat.Stat(im_crop).mean 29 red = round(mean_color[0]) ---> 30 green = round(mean_color[1]) 31 blue = round(mean_color[2]) 32 return (red, green, blue) IndexError: list index out of range
(R, G, B, A)のタプルじゃない。。
from PIL import ImageStat
mean_color = ImageStat.Stat(icon_im).mean
mean_color
[69.50645]
icon_im.getpixel((100,100))
55
(R, G, B, A)のタプルを想定
normal_im = Image.open('my_icon.png')
display_image(normal_im)
normal_im_width, normal_im_height = normal_im.size
print(normal_im_width, normal_im_height)
400 400
normal_mean_color = ImageStat.Stat(normal_im).mean
normal_mean_color
[97.509325, 85.81641875, 87.0306, 254.9675]
normal_im.getpixel((100,100))
(68, 84, 119, 255)
icon_im2 = icon_im.convert('RGBA')
icon_im2
icon_im2.getpixel((100,100))
(130, 210, 224, 255)
ImageStat.Stat(icon_im2).mean
[159.34800625, 145.5453125, 130.48335625, 248.25294375]
https://pillow.readthedocs.io/en/5.0.0/handbook/concepts.html#concept-modes
P (8-bit pixels, mapped to any other mode using a color palette)
RGBA (4x8-bit pixels, true color with transparency mask)
icon_im.mode
'P'
normal_im.mode
'RGBA'
icon_im2.mode
'RGBA'
color_data = materials_list_from_file('average_color.csv')
# icon_im = Image.open('my_icon.png')
display_image(icon_im2)
icon_im_width, icon_im_height = icon_im2.size
mosaic_icon_im = Image.new('RGBA', (1600, 1600))
for left in range(0, icon_im_width, DOT_AREA_ONE_SIDE):
for top in range(0, icon_im_height, DOT_AREA_ONE_SIDE):
average_color = calc.average_color_in_range(icon_im2, left, top,
left+DOT_AREA_ONE_SIDE, top+DOT_AREA_ONE_SIDE)
if len(average_color) != 3:
continue
filename = similar_color_filename(average_color, color_data)
# 距離最小のファイルを縮小して1600×1600の画像に貼り付け
area_im = Image.open('image/euph_part_icon/'+filename)
area_im.thumbnail((THUMBNAIL_ONE_SIDE, THUMBNAIL_ONE_SIDE))
mosaic_icon_im.paste(area_im, (left//DOT_AREA_ONE_SIDE * THUMBNAIL_ONE_SIDE,
top//DOT_AREA_ONE_SIDE * THUMBNAIL_ONE_SIDE))
save_path = 'product/slack_mosaic_mean.png'
mosaic_icon_im.save(save_path)
# Display Image ##### @@@@@ #####
#画像の読み込み
im = Image.open(save_path)
display_image(im)
→[今後] ソースコードにconvertを導入する
import csv
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from mosaic_art import calc
#Jupyterでインライン表示するための宣言
%matplotlib inline
DOT_AREA_ONE_SIDE = 10
THUMBNAIL_ONE_SIDE = 40
# 2つの色(R,G,B)の間の最大の距離
MAX_COLOR_DISTANCE = 255**2 * 3
# CSVファイル中のカラムの意味づけ
POS_NAME = 0
POS_RED = 1
POS_GREEN = 2
POS_BLUE = 3
def materials_list_from_file(filename):
"""Returns a list which contains material image information.
Args:
filename: File name such as "foo.csv"
The file contains information on average color of image.
(Average color maens the average of the values of R of all pixels,
the average of the values of G of all pixels,
and the average of the values of B of all pixels)
A row is as follows:
image_name, R_average, G_average, B_average
Returns:
A list of tuples
Tuple is like (
image_name : str (such as "bar.png"),
red_average : int,
green_average: int,
blue_average : int
)
"""
color_data = []
with open(filename, 'r', newline='') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
image_info = (row[POS_NAME], int(row[POS_RED]),
int(row[POS_GREEN]), int(row[POS_BLUE]))
color_data.append(image_info)
return color_data
def color_distance(RGB1, RGB2):
"""Returns color distance
Considering the distance between two points
(x1, y1, z1) and (x2, y2, z2) in three dimensions
Args:
RGB1: A tuple which means (R, G, B)
RGB2: A tuple which means (R, G, B)
Returns:
color distance(:int)
"""
d2_r = (RGB1[0] - RGB2[0]) ** 2
d2_g = (RGB1[1] - RGB2[1]) ** 2
d2_b = (RGB1[2] - RGB2[2]) ** 2
return d2_r + d2_g + d2_b
def similar_color_filename(average_color, color_data):
"""Returns name of file similar to average color
Find the image with average color closest to `average_color` from `color_data`
Args:
average_color: a tuple which means (R, G, B) of average color of a certain range
color_data: A list of tuples
Tuple is like (image_name, red_average, green_average, blue_average)
Returns:
A name of file such as 'foo.png' (NOT path)
"""
distance = MAX_COLOR_DISTANCE
filename = ''
# 色の差が最小になるファイルを決定(距離に見立てている)
for color in color_data:
sample_color = (color[POS_RED], color[POS_GREEN], color[POS_BLUE])
d = color_distance(average_color, sample_color)
if d < distance:
distance = d
filename = color[POS_NAME]
return filename
def display_image(image):
#画像をarrayに変換
im_list = np.asarray(image)
#貼り付け
plt.imshow(im_list)
#表示
plt.show()