はじめに
matlab
で画像処理の練習としてモザイクアートの実装を行ってみたいと思う.既存の実装例をググらずにノー知識で実装を行うため,処理が稚拙だとか滅茶苦茶になっているとか色々あると思うがご容赦願いたい.
タイトルにもある通り処理対象はグレースケール画像のみとする.こちらはカラー画像よりグレースケール画像の方が実装の難易度が低いと考えたためである.いい感じに行けたら次はカラー画像に対しても実装を行いたい.
なお画像処理を行うが,matlab
のImage Processing Toolbox
は持っていないので,あくまでmatlab
標準の関数のみを用いて実装を行う.
実装
対象となる画像の指定
% 対象の画像を指定
p = 'C:\Users\hogehoge'; % 画像の保存ディレクトリ
f = 'christmas_dance_tonakai_santa.jpg'; % 画像のファイル名
% ファイルのフルパスを作成
file = fullfile(p, f);
% ファイルを読み込む
rgbImage = imread(file);
% オリジナルの画像を表示
figure; imshow(rgbImage);
% グレースケール変換
grayImage = rgb2gray(rgbImage);
% グレースケール変換後の画像を表示
figure; imshow(grayImage);
画像についてはいらすとやの「踊るサンタクロースとトナカイのイラスト」を使用した.
前述の通りグレースケール画像に対して処理を行うため,変換してしまう.
モザイク処理
% モザイク分割
widthN = 60; % 横方向の分割数を指定する
width = floor(size(grayImage, 2)/ widthN);
resize_val = width / 1920;
tmpImage = zeros(1920, 1080);
tmpImageResized = imresize(tmpImage, resize_val);
% 縮小率の設定によってはwidthの値が1大きくなったり小さくなったりするので再定義する
width = size(tmpImageResized, 1);
height = size(tmpImageResized, 2);
grayImageMosaic = grayImage;
for n = 0:width:size(grayImage, 2)
for m = 0:height:size(grayImage, 1)
wstart = m+1;
wend = min(m+height, size(grayImage, 1));
hstart = n+1;
hend = min(n+width, size(grayImage, 2));
subImage = grayImage(wstart:wend, hstart:hend);
% 画像サブセットの輝度の平均からモザイク処理を行う
mean1 = uint8(mean(subImage(:)));
grayImageMosaic(wstart:wend, hstart:hend) = mean1;
end
end
figure; imshow(grayImageMosaic);
forループ内の画像サブセットの座標を取り出す処理は偶然にもとづくプログラミング手法を用いて実装を行っている.
モザイク処理を行った.これで読み込むデータの下準備は完了である.
画像データセットの検索
モザイク処理を行った画像の各ブロックに対して,画像データセットから近しい色の画像を探してきて当てはめていく処理を行う.
使用するデータセット
いい感じのデータセットが無かったので,とりあえず取り急ぎパソコンに大量に保存されていた お兄ちゃんはおしまい! のスクショを使用した.
検索処理の実装
function create_image_table()
% 素材を検索
pwd1 = pwd; % カレントディレクトリのバッファ
p = 'C:\Users\datasetdir'; % 検索するディレクトリ
cd(p); % ディレクトリを移動
fileList = dir('**/*.png'); % ファイルを検索
cd(pwd1); % ディレクトリを戻す
w = 0;
image_table = cell(256, 1);
for each = fileList.'
w = w + 1;
file = fullfile(each.folder, each.name);
rgbImage = imread(file);
% 1920 × 1080のみ処理
if(size(rgbImage, 1) ~= 1080 || size(rgbImage, 2) ~= 1920)
continue;
end
tmpGrayImage = rgb2gray(rgbImage);
mean1 = uint8(mean(tmpGrayImage(:))) + 1;
c1 = image_table{mean1};
im.value = mean1;
im.file = file;
if(length(c1) == 0)
image_table{mean1} = {im};
else
image_table{mean1}{length(c1) + 1} = im;
end
% データが多すぎる場合は途中で打ち切ってもいい
% if(w > 101)
% break
% end
end
fprintf('画像サーチ終わり---\n');
% 検索結果の可視化
x = 0:255;
y = zeros(256, 1);
for ii = 1:256
y(ii) = length(cell2mat(image_table{ii}));
end
figure; bar(x, y);
xlabel('輝度');
ylabel('データ数');
save_image_table(image_table);
end
function save_image_table(image_table)
save('image_table.mat');
end
構造体im
を定義し,im.value
に画像データの平均輝度(uint8=256諧調),im.file
に画像データのフルパスを格納する.
それを平均輝度(に1加算した値, 輝度は0~255だがmatlab
のインデックスは1スタートなので1~256にする)をインデックスとしてimage_table
の中にcell
として入れ子で格納する.
最後に結果をimage_table.mat
として保存する.次に使う場合にはload image_table.mat
とすればロードできる.
データセットの輝度の分布はこんな感じ.
輝度10以下とか250以上のデータが少ないけどまぁいいでしょう.
モザイクアート作成
load image_table.mat;
outputGrayImage = grayImageMosaic;
for w = 0:width:size(grayImage, 2)-width
for h = 0:height:size(grayImage, 1)-height
subImage = grayImage(h+1:h+height, w+1:w+width);
mean1 = uint8(mean(subImage(:)));
grayImage(h+1:h+height, w+1:w+width) = mean1;
c1 = image_table{mean1+1};
% ドンピシャの輝度のデータが1個も無かった場合
if(length(c1) == 0)
% 近しい輝度のデータを選ぶ
flag1 = false;
for jj=-10:10
ind1 = mean1+jj;
if(ind1 >= 1 && ind1 <= 256)
c1 = image_table{ind1};
if(length(c1) ~= 0)
flag1 = true;
break;
end
end
end
% データが見つからなかったら処理をやめて次のピクセルに行く
if(~flag1)
continue;
end
end
n1 = randi([1, length(c1)], 1);
rgbImage = imread(c1{n1}.file);
% 画像を縮小
tmpImageResized = imresize(rgbImage, resize_val);
tmpGrayImage = rgb2gray(tmpImageResized);
outputGrayImage(h+1:h+height, w+1:w+width) = tmpGrayImage;
end
end
figure; imshow(outputGrayImage);
雑な実装!
工夫した点は,もしドンピシャの輝度のデータが無かった場合にはその近辺の輝度のデータを探してくるのと,同じ輝度データのファイルが複数存在した場合にはランダムに選択するくらいかな.
なお,モザイクの一つ一つにつき毎回画像データをリサイズしているため,一回リサイズした画像はどこかにバッファしておくと処理が高速になるだろう.ほかにも色々粗があってさすがにアレなので誰かリファクタリングしてくれ.志の高い勇者を求む.
結果
サンタの顔部分を拡大するとこんな感じで,確かにおにまいのスクショで構成されていることが分かる.
真っ白い部分はデータが無かった部分だ.真っ白に近いデータは見つからなかったのだと思われる.
他の画像で作成
水星の魔女でやるとこんな感じ.戦争作品のモザイクアートを日常系作品のスクショで行うという非常にメッセージ性の強い作品ができた(大嘘).
まとめ
今回はmatlab
で画像処理の練習としてモザイクアートの作成を行った.なんとなーく実装はできたように思う.次はカラー画像に対して同様に行いたいが,どのように行ったら良いのか見当もつかない.
また,今回初めてアドベントカレンダーに参加してみた.締め切りがある中で記事を執筆する必要があり,スケジュール管理がガバったためお見せ出来ないようなコードを公開せざるを得なかった.まぁぶっちゃけ仕事でもこのくらいのクオリティで仕上げてることも多いので良いか(よくない).