LoginSignup
8
8

この記事はMATLABで行う画像処理のやり方について説明する入門となります。

はじめに

MATLABでは画像が配列として扱われるので、まず配列のことをよく理解しておいたら大分楽です。

配列に関する入門はこの前の記事で纏めていたので、まずはその記事を読むことをおすすめします。

画像処理に関する機能は一部特にパッケージを追加しなくてもすぐ使えますが、一部の関数はImage Processing Toolboxのものなのでインストールしておく必要があります。

画像を読み込む 〔imread

MATLABで画像データを扱う時に基本的にimread関数で画像ファイルを読み込んで、配列に変換します。色画像だったら「高さ×広さ×3」の3次元の配列に、無色画像だったら「高さ×広さ」の2次元の配列になります。値は0から255の8ビットの符号なし整数(uint8)。色の値はRGB(赤、緑、青)。

例としてこの記事の殆どの内容はこの画像を使うことにします。

sarada.jpg
(画像はアニメ「変人のサラダボウル」2話から)

画像を読み込んで調べてみます。

ar_img = imread("sarada.jpg");
ar_size = size(ar_img)
ar_max = max(ar_img(:))
結果
ar_size =

   405   540     3


ar_max =

  uint8

   255

このように画像はMATLABの中での配列になって、これを使って色々やることができます。

画像の基本情報を取る 〔imfinfo

画像ファイルに関する情報はimfinfo関数から取得できます。

imginfo = imfinfo("sarada.jpg")
結果
           Filename: '/Users/phyblas/Documents/MATLAB/sarada.jpg'
        FileModDate: '20-Apr-2024 13:50:23'
           FileSize: 37195
             Format: 'jpg'
      FormatVersion: ''
              Width: 540
             Height: 405
           BitDepth: 24
          ColorType: 'truecolor'
    FormatSignature: ''
    NumberOfSamples: 3
       CodingMethod: 'Huffman'
      CodingProcess: 'Sequential'
            Comment: {}

画像を表示する 〔imshow

MATLABの中で画像を表示するには普段imshowという関数を使います。使い方は色々ありますが、例えば直接画像ファイルの名前を入れたらその画像を読み込んで表示します。

imshow("sarada.jpg")

sarada_imshow.jpg

又はimreadで3次元配列に読み込んでから使うのも同じ結果です。

arimg = imread("sarada.jpg");
imshow(arimg)

無色の画像、つまり2次元配列も表示できます。例えばこの画像の赤の値だけ取って無色画像として表示します。

arimg = imread("sarada.jpg");
imshow(arimg(:,:,1))

sarada_imshow_r.jpg

画像をグラフとして表示する 〔image

imread関数を使ったらグラフを書くみたいに画像を表示することもできます。この場合アスペクト比を変えたりグリッドを行けたりするもできて使い勝手がいいですね。

figure(Position=[100 100 800 400])
image(imread("sarada.jpg"))
pbaspect([3 1 1])
grid
set(gca,GridLineStyle="--",LineWidth=2,GridColor="#660000")

sarada_image.jpg

グラフを書くことについては説明すると長くなるのでここでは割愛します。

画像を書き込む 〔imwrite

imwrite関数で配列を画像ファイルとして書き込むことができます。

例えば乱数で適当に作ってみた配列を画像として出力してみます。

arimg = rand(200,400,3);
imwrite(arimg,"randimg.jpg")

randimg.jpg

保存する配列はデータ型がdoubleでもuint8でもいいですが、doubleなら[0~1]で、uint8なら[0~255]範囲の値を使います。

2次元の配列の場合は無色の画像として保存されます。

arimg = reshape(mod(1:40000,1250)/1250,200,200);
imwrite(arimg,"rangeimg.jpg")

rangeimg.jpg

配列としての機能を駆使する

画像はただの配列だから配列の基本がしっかりわかって使いこなせていれば特に関数を使わなくても色々できます。

例えば一部だけ切り取る。

arimg = imread("sarada.jpg");
imwrite(arimg(140:240,180:270,:),"sarada_migime.jpg")

sarada_migime.jpg

明るいと暗いを逆にする。

arimg = imread("sarada.jpg");
imwrite(255-arimg,"sarada_gyaku.jpg")

sarada_gyaku.jpg

色んな数学計算をして各色の値を調整することもできます。ただしimread関数で読み込んだ配列はuint8なので、計算する時にまずデータ型をdoubleに変える必要があります。

例えば三角関数を使って波みたいな暗い縞を作ります。

arimg = double(imread("sarada.jpg"));
arimg = arimg.*(0.8+cos(linspace(0,24*pi,540))*0.2);
arimg = arimg.*(0.8+cos(linspace(0,18*pi,405)')*0.2);
imwrite(uint8(arimg),"sarada_cos.jpg")

sarada_cos.jpg

色を分ける 〔imsplit

色画像の配列はRGB3色から成されているから、どちらか使いたい時はarimg(:,:,1)arimg(:,:,2)arimg(:,:,3)のように書けばいいですね。

又はimsplitという関数を使って同時に3色を分けることもできます。

これを使って3色に分解して並んで表示してみます。

arimg = imread("sarada.jpg");
[aka,midori,ao] = imsplit(arimg);
aka = aka.*reshape(uint8([1 0 0]),1,1,3);
midori = midori.*reshape(uint8([0 1 0]),1,1,3);
ao = ao.*reshape(uint8([0 0 1]),1,1,3);
imwrite([arimg aka;midori ao],"sarada_imsplit.jpg")

sarada_imsplit.jpg

グレースケール 〔im2gray

im2grayで色画像を自然に無色(グレースケール)にすることができます。

arimg = imread("sarada.jpg");
arimg_gs = im2gray(arimg);
imwrite(arimg_gs,"sarada_gs.jpg")

sarada_gs.jpg

この関数はただRGBの平均値を取るというわけではなく、「0.299 : 0.587 : 0.114」という違う重みで計算するのです。(詳しくは公式サイト参考

自分で計算してみてもやはり関数を使うのと同じ結果が出てきます。

[aka,midori,ao] = imsplit(imread("sarada.jpg"));
arimg_gs = uint8(0.299.*aka + 0.587.*midori + 0.114.*ao);
imwrite(arimg_gs,"sarada_gs.jpg")

試しに単なる平均値を取ってみたら……。

arimg = imread("sarada.jpg");
arimg_gs = uint8(mean(arimg,3));
imwrite(arimg_gs,"sarada_gs2.jpg")

sarada_gs2.jpg

本当に結果はちょっと違います。確かにim2grayを使った方が自然に感じますね。

画像をひっくり返す 〔flip

画像を縦方向をひっくり返すにはflip関数を使うことができます。

arimg = imread("sarada.jpg");
imwrite(flip(arimg),"sarada_flip.jpg")

sarada_flip.jpg

横の方向で逆にしたい場合は2を入れて軸を指定します。

arimg = imread("sarada.jpg");
imwrite(flip(arimg,2),"sarada_flip2.jpg")

sarada_flip2.jpg

画像のサイズ(解像度)を変える 〔imresize

imresize関数によって画像を指定した[高さ 広さ]に大きくしたり小さくしたりできます。

arimg = imread("sarada.jpg");
imwrite(imresize(arimg,[300 200]),"sarada_200x300.jpg")

sarada_200x300.jpg

もしただ高さだけ指定してアスペクト比を保つまま広さも変えたい場合はNaNと書くのです。

arimg = imread("sarada.jpg");
imwrite(imresize(arimg,[300 NaN]),"sarada_300.jpg")

sarada_300.jpg

広さだけ指定したい場合も同じ。

もし指定したのは一つの数値の場合それはピクセルではなく倍数を指定するということになります。

例えば半分サイズにします。

arimg = imread("sarada.jpg");
imwrite(imresize(arimg,0.5),"sarada_x0.5.jpg")

sarada_x0.5.jpg

その他にサイズ変更する時の内挿のメソッドも指定できます。既定はbicubic(双三次内挿)を使ってこれは一番自然にできますが、bilinear(双一次内挿)やnearest(最近傍内挿)にすることができます。

例えば左目のところだけ取って拡大してみて、bicubicnearestの違いを見ます。

arimg = imread("sarada.jpg");
arimg = arimg(171:230,301:380,:);
imwrite(imresize(arimg,4),"sarada_resize_bicubic.jpg")
imwrite(imresize(arimg,4,"nearest"),"sarada_resize_nearest.jpg")

sarada_resize_bicubic.jpg
sarada_resize_nearest.jpg

こうやってnearestを利用してモザイク処理もできます。

arimg = imresize(imread("sarada.jpg"),0.1);
imwrite(imresize(arimg,10,"nearest"),"sarada_mozaiku.jpg")

sarada_mozaiku.jpg

画像を回転させる 〔imrotate

imrotate関数で画像を好きな角度で回転させることができます。

arimg = imread("sarada.jpg");
imwrite(imrotate(arimg,30),"sarada_rotate30.jpg")

sarada_rotate30.jpg

既定では回転した後何もないピクセルができてサイズが変わるが、同じ大きさのままにしたい場合は"crop"と書きます。

arimg = imread("sarada.jpg");
imwrite(imrotate(arimg,45,"crop"),"sarada_rotate45.jpg")

sarada_rotate45.jpg

画像を繰り返して並べる 〔repmat

repmat関数を使って同じ画像をコピーして何回も並べることができます。

arimg = imread("sarada.jpg");
arimg = repmat(arimg,[5 4]);
imwrite(imresize(arimg,0.25),"sarada_repmat.jpg")

sarada_repmat.jpg

パディングする 〔padarray

padarray関数で周りの空なピクセルで埋めることができます。

arimg = imread("sarada.jpg");
arimg = padarray(arimg,[15 50]);
imwrite(arimg,"sarada_padarray.jpg")

sarada_padarray.jpg

既定では0で埋められますが、"replicate"を書いたら境界のピクセルを取って繰り返すことになります。

arimg = imresize(imread("sarada.jpg"),0.5);
arimg = padarray(arimg,[111 100],"replicate");
imwrite(arimg,"sarada_padarray_replicate.jpg")

sarada_padarray_replicate.jpg

"circular"にしたら画像を循環で繰り返します。"symmetric"は鏡の反射みたいに逆の方向で繰り返します。

その他にどの方向でパディングするか指定できます。既定では"both"つまり前も後ろも。前だけにしたいなら"pre"で、後ろなら"post"。これを"circular""replicate""symmetric"と共に書くこともできます。

arimg = imresize(imread("sarada.jpg"),0.25);
arimg = padarray(arimg,[220 270],"symmetric","post");
imwrite(arimg,"sarada_padarray_symmetric.jpg")

sarada_padarray_symmetric.jpg

色空間を変える 〔rgb2hsv

普段色はRGB3色の数値で表示されていますが、他の色空間を使う場合もあります。もう一つよく使われるのはHSV、つまり色相しきそう(H)、彩度(S)、明度(V)です。詳しいことは割愛しますが、これが画像分析する時のこの空間を使う場合もあります。

rgb2hsv関数で普段のRGB空間からHSV空間に変えることができます。返される数値は0から1の小数の配列。

例えばHSVの値を分解してグラフで表示してみましょう。

arimg = imread("sarada.jpg");
arimg_hsv = rgb2hsv(arimg);

figure(Name="HSV")
tiledlayout(2,2,Padding='none',TileSpacing='tight');

nexttile
imshow(arimg_hsv(:,:,1))
title("色相")

nexttile
imshow(arimg_hsv(:,:,2))
title("彩度")

nexttile
imshow(arimg_hsv(:,:,3))
title("明度")

nexttile
imshow(arimg)
title("元の画像")

sarada_hsv.jpg

カラーマップを使う 〔ind2rgb

無色の画像を扱う時に単に黒から白で表示するよりもカラーマップを使って色にした方がいいかもしれません。

ind2rgb関数で無色画像からカラーマップで色に変換することができます。

グレースケールにされた画像に使って綺麗な色に変えてみます。

arimg = imread("sarada.jpg");
arimg_gs = im2gray(arimg);
arimg_parula = ind2rgb(arimg_gs,parula);
imwrite(arimg_parula,"sarada_parula.jpg")

sarada_parula.jpg

ただし実はimwriteimshowを使う時にわざわざind2rgbを使う必要がなく、カラーマップを入れたら自動的に変換してくれて便利です。

imwrite(arimg_gs,copper,"sarada_copper.jpg")

sarada_copper.jpg

その他にも色んなカラーマップがあるので、それぞれ作って比較してみます。

arimg_gs = im2gray(imread("sarada.jpg"));

lis_cmap = ["spring" "summer" "autumn" "winter" ...
    "jet" "parula" "copper" "turbo" ...
    "hot" "pink" "bone" "sky" "abyss","hsv"];

mkdir("saramap")
for cmap = lis_cmap
    imwrite(arimg_gs,eval(cmap),"saramap/"+cmap+".jpg")
end

こうやってこんなフォルダができます。

saramap_folder.jpg

どんなカラーマップが使えるか公式サイトで調べられます。

フォルダごと画像を読み込む 〔imageDatastore

沢山の画像が一緒のフォルダにあって全部使いたい場合はimageDatastoreを使うのも便利です。機械学習などでもよく使われています。

例えば前の例において作成したフォルダで試します。

imds = imageDatastore("saramap")
結果
imds = 

  ImageDatastore のプロパティ:

                       Files: {
                              '/Users/phyblas/Documents/MATLAB/saramap/abyss.jpg';
                              '/Users/phyblas/Documents/MATLAB/saramap/autumn.jpg';
                              '/Users/phyblas/Documents/MATLAB/saramap/bone.jpg'
                               ... and 11 more
                              }
                     Folders: {
                              '/Users/phyblas/Documents/MATLAB/saramap'
                              }
    AlternateFileSystemRoots: {}
                    ReadSize: 1
                      Labels: {}
      SupportedOutputFormats: ["png"    "jpg"    "jpeg"    "tif"    "tiff"]
         DefaultOutputFormat: "png"
                     ReadFcn: @readDatastoreImage

読み込んだらImageDatastoreのオブジェクトになって、色んなプロパティとメソッドを持っています。

.readメソッドを使ったら一つ画像を読み込んで、もう一度使ったら次の画像を読み込みます。forループとかで一枚ずつ読み込んで処理することができます。

imds = imageDatastore("saramap");
for i = 1:length(imds.Files)
    img = imds.read;
    [~,fn] = fileparts(imds.Files{i});
    sx = size(img,2);
    sy = size(img,1);
    fprintf("%10s [%d×%d]\n",fn,sx,sy)
end
結果
     abyss [540×405]
    autumn [540×405]
      bone [540×405]
    copper [540×405]
       hot [540×405]
       hsv [540×405]
       jet [540×405]
    parula [540×405]
      pink [540×405]
       sky [540×405]
    spring [540×405]
    summer [540×405]
     turbo [540×405]
    winter [540×405]

又は.readallメソッドを使って一気にcell配列として全部読み込むこともできます。

(cell配列は何なのかまだよくわからない方はこの記事を呼んでください

cell配列になるので、もし4次元の配列にしたいならちょっと工夫が必要です。これもcell配列に関して理解しておいた方がいいと思います。

imds = imageDatastore("saramap");
cellimg = imds.readall
allimg = cat(4,cellimg{:});
arrsize = size(allimg)
結果
allimg =

  14×1 cell 配列

    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}
    {405×540×3 uint8}


arrsize =

   405   540     3    14

ちなみに横で連結したいならこう書きます。

allimg = [cellimg{:}];
fprintf("%d × %d × %d\n",size(allimg))
結果
405 × 7560 × 3

縦でで連結したいならcell2mat関数を使います。

allimg = cell2mat(cellimg);
fprintf("%d × %d × %d\n",size(allimg))
結果
5670 × 540 × 3

ReadFcnというキーワードに関数を入れたら、その関数は画像が読み込まれる時にまず適用されて、その関数の返り値が使われます。

例えばグレースケールにしてサイズ変更したいならこう書きます。

function arimg = fcn(filenamae)
    arimg = imread(filenamae);
    arimg = im2gray(arimg);
    arimg = imresize(arimg,[300 NaN]);
end

imds = imageDatastore("saramap",ReadFcn=@fcn);
join(string(size(cat(4,imds.readall{:})))," × ")
結果
    "300 × 400 × 1 × 14"

画像をいっぱい並んで表示する 〔montage

沢山の画像を表示する時にmontageという関数を使うことも多いです。これを使ったら画像が四角形に並んで便利です。

montageを使う方法は色々ありますが、例えば4次元の配列に使う場合。試しに適当に作って表示してみます。

arimg = zeros(100,100,3,9,"uint8")+100;
arimg(:,:,:,1) = 0;
arimg(:,:,1,2) = 255;
arimg(:,:,2,3) = 255;
arimg(:,:,3,4) = 255;
arimg(:,:,1:2,5) = 255;
arimg(:,:,2:3,6) = 255;
arimg(:,:,[1 3],7) = 255;
arimg(:,:,:,8) = 255;
arimg(31:70,31:70,:,:) = 200;
montage(arimg)

montage0.png

imageDatastoreと使うこともできます。その場合フォルダの中の画像を全部読み込むことになります。

montage(imageDatastore("saramap"))

montage_saramap1.jpg

色々キーワードを入れて表示する方法を設定することができます。例えばSizeは縦と横に並べる数。BorderSizeは各画像の間の隙間。BackgroundColorは背景の色。

montage(imageDatastore("saramap"),Size=[4 5],BorderSize=4,BackgroundColor="y")

montage_saramap2.jpg

直接画像ファイルの名前を入れて使うこともできます。

montage(["saramap/spring.jpg" ...
    "saramap/summer.jpg" ...
    "saramap/autumn.jpg" ...
    "saramap/winter.jpg" ])

montage_saramap3.jpg

3次元の配列に使う場合、無色の画像と見做されます(その場合カラーマップを指定することもできます)。

例えば1枚だけの色画像を使ったら、3枚の無色だ認識されて、3色に分けられてしまいます。

montage(imread("sarada.jpg"),bone)

montage_sara.jpg

画像を並んで連結する〔imtile

imtilemontageと似ていますが、表示ではなく一つの大きな画像に合併するのです。

arimg = imtile(imageDatastore("saramap"),GridSize=[5 3],BorderSize=5,BackgroundColor=[0.4 0.2 0.1]);
imwrite(imresize(arimg,0.3),"saramap_imtile.jpg")

saramap_imtile.jpg

参考&もっと読む

終わりに

以上MATLABでよく使われる画像処理の基本的な機能を紹介しました。これはただ基本なので、実際にその他にもまだできることが沢山あります。

外部ライブラリ豊富のPythonと違って、MATLABの画像処理機能はほぼ全部公式のものでやり方は統一しているので、安定で覚えやすくて一旦習得したら戸惑うことなくずっと使えそうですね。

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8