320 Views
December 09, 23
スライド概要
第3回 二値化処理、画像の配列化
2018/04/26 画像情報処理特論 第3回 二値化処理、画像の配列化 東京工科大学 助教 加納 徹
授業計画 第1回:ガイダンス、Java開発環境の構築 第2回:GUIアプリケーション開発、画像データの入出力 第3回:前処理(1)(二値化処理、画像の配列化) 第4回:前処理(2)(モルフォロジー演算) 第5回:前処理(3)(平滑化、先鋭化) 第6回:画像の特徴抽出・特徴解析 第7回:画像の定量評価(MSE、PSNR、CNR) 第8回:コンピュータ診断支援への展開、まとめ 2
画像ファイル差し替えのお願い 1. 画像ファイルをダウンロード 2. 「ファイル」タブから https://kano.ac /lena.bmp 「ImageProcessing」の中に格納 gif は都合が悪かった・・・ 3
注意事項 ⚫ 前回作った GUI を一部削除します ⚫ プロジェクトを右クリックし、 [コピー] で複製しておくことを おすすめします 4
補足説明① 制御文の有効範囲について
制御文の有効範囲について ⚫ if 文 や for 文は、{ } で囲った範囲が有効 for (int y = 0; y < 100; y++) { for (int x = 0; x < 100; x++) { image.setRGB(x, y, Color.white.getRGB()); } } ⚫ 括弧内のコードが1行の場合、{ } を省略可 for (int y = 0; y < 100; y++) for (int x = 0; x < 100; x++) image.setRGB(x, y, Color.white.getRGB()); 6
補足説明② クラスのインスタンス化
クラスのインスタンス化 ⚫ クラス(抽象データ型) ⚫ 変数やメソッドのまとまり ⚫ プログラムには必ずmainクラスが一つ存在する ⚫ クラスは通常、インスタンスを生成して利用する ※ 静的 (static, final) クラスに関してはその限りではない ⚫ インスタンス(実体) ⚫ 抽象的なクラスに実際の値(実体)を持たせたもの ⚫ インスタンスを生成するには new 演算子を用いる 8 例えば、Color green = new Color(0, 255, 0);
二値化処理
二値化 (Binarization) 画像を白と黒の2階調で表現 (グレースケール・・・画像を256階調で表現) 元画像 グレースケール化 二値化
グレースケール化(復習)
グレースケール化 1. 以下のようにボタンを配置 12
グレースケール化 2. btnGrayScale をダブルクリックし、以下のソースコードを記述 private void btnGrayScaleActionPerformed(java.awt.event.MouseEvent evt) { if (bufImage == null) return; // 画像パネルが空の場合、終了 Color c; int red, green, blue, gray; // 全てのピクセルに対して処理 for (int y = 0; y < bufImage.getHeight(); y++) { for (int x = 0; x < bufImage.getWidth(); x++) { c = new Color(bufImage.getRGB(x, y)); red = c.getRed(); green = c.getGreen(); blue = c.getBlue(); gray = (red + green + blue) / 3; // 単純平均法 bufImage.setRGB(x, y, new Color(gray, gray, gray).getRGB()); } } lblDraw.setIcon(new ImageIcon(bufImage)); 13 }
グレースケール化 3. 実行結果 14
読み込みと同時にグレースケール
4. btnReadActionPerformed にグレースケール化の処理を埋め込む
private void btnReadActionPerformed(java.awt.event.ActionEvent evt) {
try {
bufImage = ImageIO.read(new File("lena.bmp")); // 画像ファイルの読み込み
Color c;
int red, green, blue, gray;
for (int y = 0; y < bufImage.getHeight(); y++) {// 全てのピクセルに対して処理
for (int x = 0; x < bufImage.getWidth(); x++) {
c = new Color(bufImage.getRGB(x, y));
red = c.getRed();
green = c.getGreen();
blue = c.getBlue();
gray = (red + green + blue) / 3;
// 単純平均法
bufImage.setRGB(x, y, new Color(gray, gray, gray).getRGB());
}
}
lblDraw.setIcon(new ImageIcon(bufImage)); // 画像の表示
} catch (IOException e) { e.printStackTrace(); }
15
}
グレースケール化ボタンの削除 5. 不要になったグレースケール化ボタンを削除 16
グレースケール化 6. 実行結果(読み込みと同時にグレースケール化) /// 17 ///
二値化処理
二値化処理 1. 以下のようにボタンを配置 19
二値化処理 2. btnBinarization をダブルクリックし、以下のソースコードを記述 private void btnBinarizationActionPerformed(java.awt.event.MouseEvent evt) { if (bufImage == null) return; // 画像パネルが空の場合、終了 Color c; int gray; // 全てのピクセルに対して処理 for (int y = 0; y < bufImage.getHeight(); y++) { for (int x = 0; x < bufImage.getWidth(); x++) { c = new Color(bufImage.getRGB(x, y)); gray = (c.getRed() + c.getGreen() + c.getBlue()) / 3; // 二値化処理 if (gray < 128) gray = 0; else gray = 255; bufImage.setRGB(x, y, new Color(gray, gray, gray).getRGB()); } } lblDraw.setIcon(new ImageIcon(bufImage)); 20 }
二値化処理 3. 実行結果 /// 21 ///
しきい値(Threshold) 4. しきい値の値をいろいろと変更してみよう private void btnBinarizationActionPerformed(java.awt.event.MouseEvent evt) { if (bufImage == null) return; // 画像パネルが空の場合、終了 Color c; int gray; // 全てのピクセルに対して処理 for (int y = 0; y < bufImage.getHeight(); y++) { for (int x = 0; x < bufImage.getWidth(); x++) { c = new Color(bufImage.getRGB(x, y)); gray = (c.getRed() + c.getGreen() + c.getBlue()) / 3; // 二値化処理 if (gray < 128) gray = 0; else gray = 255; bufImage.setRGB(x, y, new Color(gray, gray, gray).getRGB()); } } lblDraw.setIcon(new ImageIcon(bufImage)); 22 }
しきい値の変更 5. 実行結果 しきい値 = 90 しきい値 = 128 しきい値 = 180 しきい値が 0 だと真っ白、255 だと真っ黒の画像になる 23
しきい値変更UIの実装 6. 以下のようにラベルとスピナを配置 モデル型 数値型 初期値 最小値 最大値 24 : : : : : 数値 整数 128 0 256
しきい値変更UIの実装 7. 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 < bufImage.getHeight(); y++) { for (int x = 0; x < bufImage.getWidth(); x++) { c = new Color(bufImage.getRGB(x, y)); gray = (c.getRed() + c.getGreen() + c.getBlue()) / 3; // 二値化処理 if (gray < thresh) gray = 0; else gray = 255; bufImage.setRGB(x, y, new Color(gray, gray, gray).getRGB()); } } lblDraw.setIcon(new ImageIcon(bufImage)); 25 }
しきい値変更UIの実装 7. 実行結果(しきい値を変更 ⇨ 二値化ボタン) 26 元画像の値を上書きしてしまっているので 一回しか二値化処理ができない・・・ (しきい値の調整ができない)
しきい値変更UIの実装
8. btnReadActionPerformed を以下のように変更
BufferedImage bufImage, bufImageShow;
// イメージパネル
private void btnReadActionPerformed(java.awt.event.ActionEvent evt) {
try {
bufImage = ImageIO.read(new File("lena.bmp")); // 画像ファイルの読み込み
bufImageShow = new BufferedImage(bufImage.getWidth(), bufImage.getHeight(), bufImage.getType());
Color c;
int gray;
for (int y = 0; y < bufImage.getHeight(); y++) {// 全てのピクセルに対して処理
for (int x = 0; x < bufImage.getWidth(); x++) {
c = new Color(bufImage.getRGB(x, y));
gray = (c.getRed() + c.getGreen() + c.getBlue()) / 3; // 単純平均法
bufImage.setRGB(x, y, new Color(gray, gray, gray).getRGB());
}
}
bufImageShow.setData(bufImage.getData());
lblDraw.setIcon(new ImageIcon(bufImageShow)); // 画像の表示
} catch (IOException e) { e.printStackTrace(); }
}
27
しきい値変更UIの実装 9. 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 < bufImage.getHeight(); y++) { for (int x = 0; x < bufImage.getWidth(); x++) { c = new Color(bufImage.getRGB(x, y)); gray = (c.getRed() + c.getGreen() + c.getBlue()) / 3; // 二値化処理 if (gray < thresh) gray = 0; else これでしきい値の調整が可能に gray = 255; bufImageShow.setRGB(x, y, new Color(gray, gray, gray).getRGB()); } } lblDraw.setIcon(new ImageIcon(bufImageShow)); 28 }
おまけ① スピナの値変更を感知
スピナの値変更を感知 1. スピナ (spThresh) 上で右クリックし、 [イベント] ⇨ [Change] ⇨ [stateChanged] と進む 30
スピナの値変更を感知 2. spThreshStateChanged に以下のソースコードを記入 private void spThreshStateChanged(java.awt.event.ChangeEvent evt) { // ボタン (btnBinarization) の処理を呼び出し btnBinarizationActionPerformed(null); } 3. 実行結果 31
おまけ② スライダとスピナの同期
スライダとスピナの同期 1. 以下のようにスライダを配置 minimum : 0 maximum : 256 value : 128 33
スライダとスピナの同期 2. スライダ (slThresh) 上で右クリックし、 [イベント] ⇨ [Change] ⇨ [stateChanged] と進む 34
スライダとスピナの同期 3. slThreshStateChanged に以下のソースコードを記入 private void slThreshStateChanged(java.awt.event.ChangeEvent evt) { // スピナの値を更新 spThresh.setValue(slThesh.getValue()); } 4. spThreshStateChanged に以下のソースコードを記入 private void spThreshStateChanged(java.awt.event.ChangeEvent evt) { // スライダの値を更新 slThesh.setValue((Integer) spThresh.getValue()); // ボタン (btnBinarization) の処理を呼び出し btnBinarizationActionPerformed(null); } 35
スライダとスピナの同期 5. 実行結果 (スライダを移動させるとスピナの数値が変化し、画像も更新される) スピナの値を変更すると、それに合わせてスライダも移動するよ 36
おまけ③ ラベルより大きい画像の読み込み
ラベルより大きい画像の読み込み 1. ラベル (lblDraw) 上で右クリックし、 [含める] ⇨ [スクロールペイン] と進む 38 今回は必要に応じてスクロールバーを出す方法を紹介
ラベルより大きい画像の読み込み 2. 左下のナビゲータから lblDraw の 上階層にある jScrollPane を選択 (名前を必要に応じて変更する) 39 3. プロパティからデフォルトの 横方向サイズと縦方向サイズを入力 【注意】512x512 をデフォルトにしたい場合は、 jScrollPane の境界線を考慮して 514x514 にする
ラベルより大きい画像の読み込み 4. 大きい Lena の画像 (1024x1024) を読み込んだ結果 40
画像の配列化
変数 変数の型 変数の名前 値 int score = 50; 5人分の点数を扱いたい・・・ int score1, score2, score3・・・ 42 つらい・・・
配列 配列の型 配列の名前 要素数 int[] score = new int[5]; int 型の変数 score[0], score[1], score[2], score[3], score[4] が生成される! 43 要素数が 5 のとき、添字は 0~4 の 5 つ!
配列の初期化 各要素への数値の代入 int[] score = new int[5]; score[0] = 50; score[1] = 80; score[2] = 70; score[3] = 100; score[4] = 30; 宣言と初期化の一元化 int[] score = {50, 80, 70, 100, 30}; 44 初期化しなかった場合、各要素には 0 が格納される
画像の配列化 以下のような 4 x 4 の画像(グレー)情報を 配列に格納することを考える X (3,0) (0,0) Y (0,3) 45 20 40 80 10 10 50 70 10 40 0 0 40 80 70 60 50 (3,3) 画像は通常、左上が原点の座標系
画像の配列化 画像情報を保存するには16要素の配列が必要 int[] image = new int[16]; 0 1 2 3 4 20 40 80 10 5 6 7 8 9 10 50 70 10 40 0 10 11 0 40 12 13 14 15 80 70 60 50 (0,0) の情報・・・image[0] = 20 (1,0) の情報・・・image[1] = 40 (0,1) の情報・・・image[4] = 10 (2,2) の情報・・・image[10] = 0 (3,3) の情報・・・image[15] = 50 46 (x,y) の情報・・・image[????]
画像の配列化 画像情報を保存するには16要素の配列が必要 int[] image = new int[16]; 0 1 2 3 4 20 40 80 10 5 6 7 8 9 10 50 70 10 40 0 10 11 0 40 12 13 14 15 80 70 60 50 (0,0) の情報・・・image[0] = 20 (1,0) の情報・・・image[1] = 40 (0,1) の情報・・・image[4] = 10 (2,2) の情報・・・image[10] = 0 (3,3) の情報・・・image[15] = 50 47 (x,y) の情報・・・image[y * 4 + x]
画像の配列化 画像情報を保存するには16要素の配列が必要 int[] image = new int[16]; 0 1 2 3 4 20 40 80 10 5 6 7 8 9 10 50 70 10 40 0 10 11 0 40 12 13 14 15 80 70 60 50 (0,0) の情報・・・image[0] = 20 (1,0) の情報・・・image[1] = 40 (0,1) の情報・・・image[4] = 10 (2,2) の情報・・・image[10] = 0 (3,3) の情報・・・image[15] = 50 48 (x,y) の情報・・・image[y * WIDTH + x]
画像の配列化 画像配列の (x,y) 座標の値を変更したい場合 image[y * imageWidth + x] = 100; 画像配列の (x,y) 座標の値を参照したい場合 System.out.println(image[y * imageWidth + x]); 画像配列の全座標値を変更したい場合 for (int y = 0; y < imageHeight; y++) { for (int x = 0; x < imageWidth; x++) { image[y * imageWidth + x] = 100; } } 49
注意事項 ⚫ 前回書いたソースコードを一部削除します ⚫ プロジェクトを右クリックし、 [コピー] で複製しておくことを おすすめします 50
画像の配列化 1. フィールド(メソッドの外)で以下の変数を宣言 final int MAX_WIDTH = 1024, MAX_HEIGHT = 1024; int imageWidth, imageHeight; int[] imageData = new int[MAX_WIDTH * MAX_HEIGHT]; BufferedImage bufImage, bufImageShow; private void btnReadActionPerformed(java.awt.event.ActionEvent evt) { try { bufImage = ImageIO.read(new File("lena.bmp")); // 画像ファイルの読み込み … … 51
画像の配列化
2. btnReadActionPerformed を以下のように変更
try {
bufImage = ImageIO.read(new File("lena.bmp")); // 画像ファイルの読み込み
imageWidth = bufImage.getWidth();
imageHeight = bufImage.getHeight();
bufImageShow = new BufferedImage(imageWidth, imageHeight, bufImage.getType());
Color c;
int gray;
for (int y = 0; y < imageHeight; y++) {// 全てのピクセルに対して処理
for (int x = 0; x < imageWidth; x++) {
c = new Color(bufImage.getRGB(x, y));
gray = (c.getRed() + c.getGreen() + c.getBlue()) / 3; // 単純平均法
imageData[y * imageWidth + x] = gray;
bufImage.setRGB(x, y, new Color(gray, gray, gray).getRGB());
}
}
bufImageShow.setData(bufImage.getData());
lblDraw.setIcon(new ImageIcon(bufImageShow)); // 画像の表示
52
} catch (IOException e) { e.printStackTrace(); }
画像の配列化 3. 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)); } 53
宿題 インターネットから「CT画像」を1枚ダウンロード して、ソフトウェアに読み込ませなさい。さらに、 しきい値の調整によって骨領域のみの画像にしなさい。 【提出物】以下の2枚のスクリーンショット ⚫ CT画像を読み込ませた状態の画面 ⚫ CT画像を骨領域のみに処理した状態の画面 (提出物例:oroginal.png, bone.png) 54
お疲れ様でした つづく