先月行われたMATLAB Central 20周年記念の MATLAB Mini Hack.280文字以内のMATLABコードでどれだけ美しい画像が描けるか,というとても面白いコンテストでした.1700近くのエントリーがありました.本当に280字以内で描いたのか,というものがたくさんありました.
私はあまり数学的な美しい図は思いつかなかったので,MATLABのロゴを使ったモザイクアートを作ってみました.ここでそのコードを解説します.
コンテストで使ったコードはこちらになります:Mosaic Membrane
ただ,その後もう少し考えてみたらもっと簡単にできることが判明しました.今回は簡単な方を紹介します.
logo;
c = getframe;
im = im2double(c.cdata);
im2 = imresize(im, 1/8);
im3 = imresize(im, [20 20]);
im4 = imresize(im, [1 1]);
im_diff = im2 - im4;
[r,c,~] = size(im2);
for i1 = 1:r
for i2 = 1:c
out{i1,i2} = im3 + im_diff(i1,i2,:);
end
end
imshow(cell2mat(out))
解説
1. MATLAB ロゴ
まずはモザイクを作る画像とベースとなるタイル(今回は両方とも同じ画像)の MATLAB ロゴの画像を用意するのですが,標準デモ関数(スクリプト)の logo
を使って,表示されるロゴのスクリーンキャプチャーを getframe
関数で取得します.
logo;
c = getframe
c =
cdata: [840x818x3 uint8]
colormap: []
c
は構造体で,その中の cdata
フィールドに画像データが含まれています.通常 uint8
型で定義してありますが,後にいろいろ演算を行うので double
型に変換します.
im = im2double(c.cdata);
whos im
Name Size Bytes Class Attributes
im 840x818x3 16490880 double
2. モザイク用画像の準備
モザイク画像とは,それぞれのピクセルを別の画像で表現して作り上げるので,最終的にできあがる画像はもとの画像よりも大きくなります.よって,今回は処理時間を短縮するために元の画像の大きさを小さくします.
im2 = imresize(im, 1/8); % 1/8の大きさ
whos im2
Name Size Bytes Class Attributes
im2 105x103x3 259560 double
また,各ピクセルを作り上げる画像の大きさを設定します.今回は 20 x 20 の大きさに調整します.
im3 = imresize(im, [20 20]);
whos im3
Name Size Bytes Class Attributes
im3 20x20x3 9600 double
つまり,最終画像は 2100 x 2060 の大きさになります.
3. ピクセルに近似させるには
モザイク画像を作成するためには,タイルとして使う画像をうまく組み合わせて元の画像を再現するのですが,タイルとして使う画像が MATLAB ロゴの一枚となるとどうすればよいのでしょうか.
それを実現するためにロゴの画像の色を調整しました.
例えば,ロゴに赤み,緑み,青みを増すには
imR = im2 + reshape([0.5 0 0],1,1,3);
imG = im2 + reshape([0 0.5 0],1,1,3);
imB = im2 + reshape([0 0 0.5],1,1,3);
figure, tiledlayout(1,3,"TileSpacing","tight","Padding","tight")
nexttile
imshow(imR), title("赤み","Color","red")
nexttile
imshow(imG), title("緑み","Color","red")
nexttile
imshow(imB), title("青み","Color","red")
4. 色の距離の計算
色みを付ける方法が分かったので,後はそれぞれのタイルの色をどの程度調整するかを計算するだけです.
まずは,タイルの "平均の色" を計算するために,画像を1ピクセルに縮小します.
im4 = imresize(im,[1 1])
im4 =
im4(:,:,1) =
0.2236
im4(:,:,2) =
0.1066
im4(:,:,3) =
0.0474
figure, imshow(im4)
なるほど.背景が黒いので全体的に黒いですね.ちょっとオレンジ・茶色がかっていますね.
さて,この色からもともとのロゴの画像の各ピクセルの色との差を計算すればよいのです.
im_diff = im2 - im4;
whos im_diff
Name Size Bytes Class Attributes
im_diff 105x103x3 259560 double
im_diff
はもとの画像と同じ大きさで,各ピクセルの値は1ピクセルロゴとの色の差を表しています.
5. モザイク画像の組み立て
これで準備完了.最後にそれぞれのピクセルをタイル(+上で求めた色の差分)に置き換えれば完了です.ただし,タイルは 20 x 20 の大きさなので単純に1ピクセルを置き換えるわけにはいかないので,セル配列で集約します.
[r,c,~] = size(im2);
for i1 = 1:r
for i2 = 1:c
out{i1,i2} = im3 + im_diff(i1,i2,:);
end
end
% 最初の3行3列を見てみよう
out(1:3,1:3)
1 | 2 | 3 | |
---|---|---|---|
1 | 20x20x3 double | 20x20x3 double | 20x20x3 double |
2 | 20x20x3 double | 20x20x3 double | 20x20x3 double |
3 | 20x20x3 double | 20x20x3 double | 20x20x3 double |
最後にすべてをくっつけます.
imOut = cell2mat(out);
whos imOut
Name Size Bytes Class Attributes
imOut 2100x2060x3 103824000 double
imshow(imOut)
おしまい
試しに2階層のモザイクアートを作ってみました.ロゴの原型が分かるようにすると画素数が大きくなってしまい,ちょっと処理が重くなりましたが,もっと性能の良いPCだったらいろいろ楽しめると思います.