2.3K Views
December 09, 23
スライド概要
第4回 モルフォロジー演算
2018/05/10 画像情報処理特論 第4回 モルフォロジー演算 東京工科大学 助教 加納 徹
授業計画 第1回:ガイダンス、Java開発環境の構築 第2回:GUIアプリケーション開発、画像データの入出力 第3回:前処理(1)(二値化処理、画像の配列化) 第4回:前処理(2)(モルフォロジー演算) 第5回:前処理(3)(平滑化、先鋭化) 第6回:画像の特徴抽出・特徴解析 第7回:画像の定量評価(MSE、PSNR、CNR) 第8回:コンピュータ診断支援への展開、まとめ 2
注意事項 ⚫ 前回作った GUI を一部削除します ⚫ プロジェクトを右クリックし、 [コピー] で複製しておくことを おすすめします 3
補足説明① 論理演算子について
論理演算子ついて 複雑な条件を指定したい場合は、論理演算子を用いる 演算子 意味 (条件A) && (条件B) A と B のどちらも真 (条件A) || (条件B) A と B のどちらかが真 使用例 if (60 <= score && score < 70) { // score が 60 以上 70 未満のとき実行 } if (score < 0 || 100 < score) { // score が 0 未満 または 100 より大きいとき実行 5 }
補足説明② 色変数の定義による高速化
前回のソースコード btnBinarizationActionPerformed (二値化ボタン)の中身 private void btnBinarizationActionPerformed(java.awt.event.MouseEvent evt) { if (bufImage == null) return; // 画像パネルが空の場合、終了 Color c; int gray, thresh = (Integer) spThresh.getValue(); // 全てのピクセルに対して処理 for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { gray = imageData[y * imageWidth + x]; // 二値化処理 if (gray < thresh) gray = 0; else gray = 255; bufImageShow.setRGB(x, y, new Color(gray, gray, gray).getRGB()); } } lblDraw.setIcon(new ImageIcon(bufImageShow)); } 7
フィールドで以下の変数を宣言 フィールド・・・メソッドの外(クラス内のどのメソッドからも参照可能) final int MAX_WIDTH = 1024, MAX_HEIGHT = 1024; int imageWidth, imageHeight; int[] imageData = new int[MAX_WIDTH * MAX_HEIGHT]; int black = Color.black.getRGB(); int white = Color.white.getRGB(); BufferedImage bufImage, bufImageShow; private void btnReadActionPerformed(java.awt.event.ActionEvent evt) { try { bufImage = ImageIO.read(new File("lena.bmp")); // 画像ファイルの読み込み … … 8
前回のソースコード btnBinarizationActionPerformed (二値化ボタン)の中身 private void btnBinarizationActionPerformed(java.awt.event.MouseEvent evt) { if (bufImage == null) return; // 画像パネルが空の場合、終了 int gray, thresh = (Integer) spThresh.getValue(); // 全てのピクセルに対して処理 for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { gray = imageData[y * imageWidth + x]; // 二値化処理 if (gray < thresh) bufImageShow.setRGB(x, y, black); else bufImageShow.setRGB(x, y, white); } } lblDraw.setIcon(new ImageIcon(bufImageShow)); } 隙あらば処理の効率化 9
事前準備 二値化処理用配列の宣言
フィールドで以下の変数を宣言 final int MAX_WIDTH = 1024, MAX_HEIGHT = 1024; int imageWidth, imageHeight; int[] imageData = new int[MAX_WIDTH * MAX_HEIGHT]; int[] imageDataBinary = new int[MAX_WIDTH * MAX_HEIGHT]; int[] imageDataBinaryBuffer = new int[MAX_WIDTH * MAX_HEIGHT]; int black = Color.black.getRGB(); int white = Color.white.getRGB(); BufferedImage bufImage, bufImageShow; private void btnReadActionPerformed(java.awt.event.ActionEvent evt) { try { bufImage = ImageIO.read(new File("lena.bmp")); // 画像ファイルの読み込み … … 11
二値化処理結果を配列に格納 btnBinarizationActionPerformed を以下のように変更 private void btnBinarizationActionPerformed(java.awt.event.MouseEvent evt) { if (bufImage == null) return; // 画像パネルが空の場合、終了 int gray, thresh = (Integer) spThresh.getValue(); for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { gray = imageData[y * imageWidth + x]; if (gray < thresh) { bufImageShow.setRGB(x, y, black); imageDataBinary[y * imageWidth + x] = 0; } else { bufImageShow.setRGB(x, y, white); imageDataBinary[y * imageWidth + x] = 255; } } } lblDraw.setIcon(new ImageIcon(bufImageShow)); 12 }
モルフォロジー演算
モルフォロジー (Morphology) Morphology・・・形態学、形態論 二値画像に対する形状ベースの画像処理操作の総称 ⚫ 構造化要素(Structuring Element) ⚫ 膨張(Dilation) ⚫ 収縮(Erosion) ⚫ オープニング(Opening) ⚫ クロージング(Closing) 14 今日はこの5つを理解して実装しよう
構造化要素
構造化要素 ⚫ 画像の形状をどのように変化させるかを決める要素 Disk, 3x3 Square, 3x3 Disk, 5x5 0 1 0 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 3x3 の Disk は Diamond や Rhombus とも呼ばれる
膨張(Dilation)
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理 ・・・構造化要素(Disk, 3x3)
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理 Miss
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理 Hit 構造化要素と一点でも重なっていたら Hit!
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理
膨張(Dilation) 二値画像を構造化要素に従って膨張させる処理 ・・・構造化要素(Disk, 3x3) 膨張処理によって、隙間が塗りつぶされる
膨張処理の実装 これからは GUI も自分好みに実装していってみよう 27
膨張処理の実装
btnDilationActionPerformed を以下のように変更
private void btnDilationActionPerformed(java.awt.event.MouseEvent evt) {
if (bufImage == null) return; // 画像パネルが空の場合、終了
for (int y = 1; y < imageHeight - 1; y++) {
for (int x = 1; x < imageWidth - 1; x++) {
if (imageDataBinary[y * imageWidth + x] == 255
|| imageDataBinary[y * imageWidth + x - 1] == 255
|| imageDataBinary[y * imageWidth + x + 1] == 255
|| imageDataBinary[(y - 1) * imageWidth + x] == 255
|| imageDataBinary[(y + 1) * imageWidth + x] == 255) {
bufImageShow.setRGB(x, y, white);
} else {
bufImageShow.setRGB(x, y, black);
}
}
}
lblDraw.setIcon(new ImageIcon(bufImageShow));
28
}
膨張処理の実行結果 二値化処理 膨張処理 ちょっとだけ白い領域が膨張した 29
収縮(Erosion)
収縮(Erosion) 二値画像を構造化要素に従って収縮させる処理 ・・・構造化要素(Disk, 3x3)
収縮(Erosion) 二値画像を構造化要素に従って膨張させる処理 Hit 構造化要素と一点でも重なっていたら Hit, だけど・・・
収縮(Erosion) 二値画像を構造化要素に従って膨張させる処理
収縮(Erosion) 二値画像を構造化要素に従って膨張させる処理
収縮(Erosion) 二値画像を構造化要素に従って膨張させる処理
収縮(Erosion) 二値画像を構造化要素に従って膨張させる処理
収縮(Erosion) 二値画像を構造化要素に従って膨張させる処理 Fit 構造化要素の全ての点が重なっている場合は、Fit!
収縮(Erosion) 二値画像を構造化要素に従って収縮させる処理 ・・・構造化要素(Disk, 3x3) ・・・ Hit ・・・ Fit Hit の領域は消去、Fit の領域のみを残す
収縮(Erosion) 二値画像を構造化要素に従って収縮させる処理 ・・・構造化要素(Disk, 3x3) ・・・ Hit ・・・ Fit 収縮処理によって尖った部分が侵食される
収縮処理の実装
btnErosionActionPerformed を以下のように変更
private void btnErosionActionPerformed(java.awt.event.MouseEvent evt) {
if (bufImage == null) return; // 画像パネルが空の場合、終了
for (int y = 1; y < imageHeight - 1; y++) {
for (int x = 1; x < imageWidth - 1; x++) {
if (imageDataBinary[y * imageWidth + x] == 255
&& imageDataBinary[y * imageWidth + x - 1] == 255
&& imageDataBinary[y * imageWidth + x + 1] == 255
まずは自分の力で考えてみよう
&& imageDataBinary[(y - 1) * imageWidth + x] == 255
&& imageDataBinary[(y + 1) * imageWidth + x] == 255) {
bufImageShow.setRGB(x, y, white);
} else {
bufImageShow.setRGB(x, y, black);
}
}
}
lblDraw.setIcon(new ImageIcon(bufImageShow));
40
}
収縮処理の実装
btnErosionActionPerformed を以下のように変更
private void btnErosionActionPerformed(java.awt.event.MouseEvent evt) {
if (bufImage == null) return; // 画像パネルが空の場合、終了
for (int y = 1; y < imageHeight - 1; y++) {
for (int x = 1; x < imageWidth - 1; x++) {
if (imageDataBinary[y * imageWidth + x] == 255
&& imageDataBinary[y * imageWidth + x - 1] == 255
&& imageDataBinary[y * imageWidth + x + 1] == 255
&& imageDataBinary[(y - 1) * imageWidth + x] == 255
&& imageDataBinary[(y + 1) * imageWidth + x] == 255) {
bufImageShow.setRGB(x, y, white);
} else {
bufImageShow.setRGB(x, y, black);
}
}
}
lblDraw.setIcon(new ImageIcon(bufImageShow));
41
}
収縮処理の実行結果 二値化処理 収縮処理 ちょっとだけ白い領域が収縮した 42
オープニング(Opening)
オープニング(Opening) 収縮の後に膨張 をする処理 ・・・構造化要素(Disk, 3x3)
オープニング(Opening) 収縮の後に膨張 をする処理 ・・・構造化要素(Disk, 3x3) 収縮処理
オープニング(Opening) 収縮の後に膨張 をする処理 ・・・構造化要素(Disk, 3x3) 収縮処理
オープニング(Opening) 収縮の後に膨張 をする処理 ・・・構造化要素(Disk, 3x3) 収縮処理 膨張処理
オープニング(Opening) 収縮の後に膨張 をする処理 元画像 オープニング処理 尖った部分が消えた!ノイズのような点も無くなった
膨張処理の拡張
private void btnDilationActionPerformed(java.awt.event.MouseEvent evt) {
if (bufImage == null) return; // 画像パネルが空の場合、終了
for (int y = 1; y < imageHeight - 1; y++) {
for (int x = 1; x < imageWidth - 1; x++) {
if (imageDataBinary[y * imageWidth + x] == 255
|| imageDataBinary[y * imageWidth + x - 1] == 255
|| imageDataBinary[y * imageWidth + x + 1] == 255
|| imageDataBinary[(y - 1) * imageWidth + x] == 255
|| imageDataBinary[(y + 1) * imageWidth + x] == 255) {
bufImageShow.setRGB(x, y, white);
imageDataBinaryBuffer[y * imageWidth + x] = 255;
} else {
bufImageShow.setRGB(x, y, black);
imageDataBinaryBuffer[y * imageWidth + x] = 0;
}
}
}
imageDataBinary = Arrays.copyOf(imageDataBinaryBuffer, imageData.length);
lblDraw.setIcon(new ImageIcon(bufImageShow));
}
49
収縮処理の拡張
private void btnErosionActionPerformed(java.awt.event.MouseEvent evt) {
if (bufImage == null) return; // 画像パネルが空の場合、終了
for (int y = 1; y < imageHeight - 1; y++) {
for (int x = 1; x < imageWidth - 1; x++) {
if (imageDataBinary[y * imageWidth + x] == 255
&& imageDataBinary[y * imageWidth + x - 1] == 255
&& imageDataBinary[y * imageWidth + x + 1] == 255
&& imageDataBinary[(y - 1) * imageWidth + x] == 255
&& imageDataBinary[(y + 1) * imageWidth + x] == 255) {
bufImageShow.setRGB(x, y, white);
imageDataBinaryBuffer[y
* imageWidth + x] = 255;
膨張処理と同様
} else {
bufImageShow.setRGB(x, y, black);
imageDataBinaryBuffer[y
* imageWidth + x] = 0;
膨張処理と同様
}
}
}
imageDataBinary = Arrays.copyOf(imageDataBinaryBuffer,
imageData.length);
膨張処理と同様
lblDraw.setIcon(new ImageIcon(bufImageShow));
}
50
オープニング処理 [収縮] ボタン ⇨ [膨張] ボタン 二値化画像 51 オープニング処理画像
オープニング処理 [収縮] ボタン n 回 ⇨ [膨張] ボタン n 回 繰り返し適用 構造化要素(Disk, 3x3) 構造化要素(Diamond, 5x5) お手軽~ 52
オープニング処理 [収縮] ボタン3回 ⇨ [膨張] ボタン3回 二値化画像 53 オープニング処理画像 細かい点や細い線、尖った部分が無くなった
クロージング(Closing)
クロージング(Closing) 膨張の後に収縮 をする処理 ・・・構造化要素(Disk, 3x3)
クロージング(Closing) 膨張の後に収縮 をする処理 ・・・構造化要素(Disk, 3x3) 膨張処理
クロージング(Closing) 膨張の後に収縮 をする処理 ・・・構造化要素(Disk, 3x3) 膨張処理
クロージング(Closing) 膨張の後に収縮 をする処理 ・・・構造化要素(Disk, 3x3) 膨張処理 収縮処理
クロージング(Closing) 収縮の後に膨張 をする処理 元画像 オープニング処理 穴が埋まった!細かい溝も無くなった
クロージング処理 [膨張] ボタン3回 ⇨ [収縮] ボタン3回 二値化画像 60 オープニング処理画像 小さい穴や細い溝が埋まった!
宿題 以下の課題を Word で作成し、PDF形式で提出しなさい。 (ファイル名:学籍番号_氏名.pdf) 【課題1】 二値化した「CT画像」に対してオープニング処理、クロージング 処理を適用し、より正確な骨のみの画像を作りなさい。 【課題2】 講義で扱っていない構造化要素を新たに実装し、実行結果につい て考察しなさい。 【発展課題】 [オープニング] ボタン、[クロージング] ボタンを実装しなさい。 また、コンボボックスを用いて構造化要素を選択できるようにしな さい。 ※適宜スクリーンショットや処理画像を掲載すること 61
お疲れ様でした つづく