【MATLAB】モザイクアートを手実装する（モノクロ画像編）

# はじめに
`matlab`で画像処理の練習としてモザイクアートの実装を行ってみたいと思う．既存の実装例をググらずにノー知識で実装を行うため，処理が稚拙だとか滅茶苦茶になっているとか色々あると思うがご容赦願いたい．
タイトルにもある通り処理対象はグレースケール画像のみとする．こちらはカラー画像よりグレースケール画像の方が実装の難易度が低いと考えたためである．いい感じに行けたら次はカラー画像に対しても実装を行いたい．
なお画像処理を行うが，`matlab`の`Image Processing Toolbox`は持っていないので，あくまで`matlab`標準の関数のみを用いて実装を行う．

# 実装
## 対象となる画像の指定

```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);
```

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/e3af037b-0a9e-cc14-a863-ac51eedc7c87.png">

画像についてはいらすとやの「[踊るサンタクロースとトナカイのイラスト](https://www.irasutoya.com/2016/12/blog-post_374.html)」を使用した．

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/7e889f22-ce0b-eb2e-bc28-920efadba095.png">

前述の通りグレースケール画像に対して処理を行うため，変換してしまう．


### モザイク処理
```matlab
% モザイク分割
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ループ内の画像サブセットの座標を取り出す処理は**偶然にもとづくプログラミング手法**を用いて実装を行っている．



<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/266fcb09-c6bb-9373-e02a-a6cec134b348.png">


モザイク処理を行った．これで読み込むデータの下準備は完了である．

## 画像データセットの検索
モザイク処理を行った画像の各ブロックに対して，画像データセットから近しい色の画像を探してきて当てはめていく処理を行う．




### 使用するデータセット
いい感じのデータセットが無かったので，とりあえず取り急ぎパソコンに大量に保存されていた **お兄ちゃんはおしまい！** のスクショを使用した．

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/ea0fdffe-9cd6-fba3-39e4-bd3a82c7c5de.png"><br>

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/29509735-067b-5f1c-c71e-82c2c1415a90.png"><br>
4516枚も画像があったので十分だと思われる．


### 検索処理の実装

```matlab
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`とすればロードできる．


![fig5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/68ce898d-1dd7-ffc8-6d2a-2d9045282215.png)
データセットの輝度の分布はこんな感じ．
輝度10以下とか250以上のデータが少ないけどまぁいいでしょう．


# モザイクアート作成

```matlab
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);
```

雑な実装！　
工夫した点は，もしドンピシャの輝度のデータが無かった場合にはその近辺の輝度のデータを探してくるのと，同じ輝度データのファイルが複数存在した場合にはランダムに選択するくらいかな．

なお，モザイクの一つ一つにつき毎回画像データをリサイズしているため，一回リサイズした画像はどこかにバッファしておくと処理が高速になるだろう．ほかにも色々粗があってさすがにアレなので誰かリファクタリングしてくれ．志の高い勇者を求む．

## 結果
![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/fa30d808-9684-12ff-d907-2f360af00269.png)
結果はこんな感じ．なんとなく上手く行っているように思う．

![output2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/b7e4509f-bd29-f0b2-8a36-5befd2e80d2e.png)
サンタの顔部分を拡大するとこんな感じで，確かにおにまいのスクショで構成されていることが分かる．
真っ白い部分はデータが無かった部分だ．真っ白に近いデータは見つからなかったのだと思われる．


## 他の画像で作成
![g1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/f0de238a-5a1c-6a7e-b63c-976632d0c847.png)

![g2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/652e3439-98ce-8aca-6167-1086ffb2cff9.png)

水星の魔女でやるとこんな感じ．戦争作品のモザイクアートを日常系作品のスクショで行うという**非常にメッセージ性の強い作品**ができた（大嘘）．


![g3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/954418aa-8406-70f6-8a75-0261753a3da2.png)
ちなみに目の部分を拡大するとこんな感じ．


# まとめ
今回は`matlab`で画像処理の練習としてモザイクアートの作成を行った．なんとなーく実装はできたように思う．次はカラー画像に対して同様に行いたいが，どのように行ったら良いのか見当もつかない．
また，今回初めてアドベントカレンダーに参加してみた．締め切りがある中で記事を執筆する必要があり，スケジュール管理がガバったためお見せ出来ないようなコードを公開せざるを得なかった．まぁぶっちゃけ仕事でもこのくらいのクオリティで仕上げてることも多いので良いか（よくない）．