Java OpenCV Lv9 〜画像編集「足し算」(cvAdd)〜

イントロダクション

やってきました、大敵!今回は「行列計算処理」をやります。そして今までのやったことを少し整理します。

下のリンクにあるように環境構築に始まり

  1. 画像の表示
  2. 画像の平滑化処理(smooth(), gaussianBlur(), medianBlur())
  3. 描画処理(線を引く、Ellipse処理(中途半端ですが。。。)
  4. 部分的な画像修正(描画)処理

という順番で学習してきました。そして、今回は画像=行列(Matクラス)の処理を少し掘り進んでみようというわけです。

環境構築

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java Install Eclipse〜開発ツールのインストール〜
  3. Java OpenCv Hello in Java〜OpenCv事始め〜

今までにやったこと(OpenCV編)

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java OpenCv Lv1 〜入門: 写真の表示〜
  3. Java OpenCV Lv2 〜画像を表示する〜
  4. Java OpenCV Lv3 〜画像の平滑化(smooth())〜
  5. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())〜
  6. Java OpenCV Lv4 〜画像の中身をみてみる〜
  7. Java OpenCV Lv5 〜Matクラスで描画処理〜
  8. Java OpenCV Lv6 〜Matクラスで背景から作成してみる〜
  9. Java OpenCV Lv7 〜MatクラスでEllipseしてみる〜
  10. Java OpenCV Lv9 〜画像編集「足し算」(cvAdd)〜
  11. Java OpenCV Lv9 〜画像編集「引き算」(cvSubtract)〜
  12. Java OpenCV Lv9 〜画像の掛け算〜
  13. Java OpenCV Lv10 〜行列演算Mat#submat()〜
  14. Java OpenCv Lv10〜画像の平均値をだす〜

重宝するページ:JavaDoc(opencv-343.jar API)

行列=Matクラスの四則計算

まずはデフォルト表示

<実行結果:画像表示の確認>

まずは、そのままの(加算処理をしない状態の)画像を確認します。

<ソース1>

public static void main(String[] args) {
	long start = System.currentTimeMillis();
	// 100x100の白いPNGからのデータを作成する
	Mat src = Imgcodecs.imread(OpenCVTest9_Add.class.getResource("/images/Experience.png").getPath());
	ViewFrame frame = new ViewFrame(src);
	System.out.println("実行時間: " + (System.currentTimeMillis() - start) + "ミリ秒");
}

シンプルに画像を読み込んで、SwingのLabelに表示するだけです。

加算処理

<加算する画像>事情により自分で作成したため微妙ですが。。。

<参考サイト>

画像を合体させる

Java API(OpenCV)#cvAdd ※Coreクラスにありました。

<実行結果:行列(Mat)に下の画像を追加します>

エラーになりました。以下のような文言が出力されました。

neither 'array op array' (where arrays have 
the same size and the same number of channels)
, nor 'array op scalar'
, nor 'scalar op array' in function 'arithm_op'



そんなわけで、イメージファイルを変更します。

元にするファイル=>

加算するファイル=>

ちなみに出力する画像データを見ている関係で画像ファイルが小さいです。(50x50)

<実行結果>

ソース2>※Gitからダウンロードできます

public static void main(String[] args) {
	long start = System.currentTimeMillis();
	// 100x100の白いPNGからのデータを作成する
	Mat src = Imgcodecs.imread(OpenCVTest9_Add.class.getResource("/images/cart2.png").getPath());
	Mat cart = Imgcodecs.imread(OpenCVTest9_Add.class.getResource("/images/rakugaki.png").getPath());
	Mat dst = new Mat();
	Core.add(src, cart, dst);
	ViewFrame frame = new ViewFrame(dst);
	System.out.println("実行時間: " + (System.currentTimeMillis() - start) + "ミリ秒");
}

赤い字の部分をソースに追加しました。そして、ほとんど消えてしまいました。

足し算したはずなのになんででしょうか?

元のイメージのデータ(Matの中身)※一部

足し算後のデータ ※一部

単純に足し算した結果値が「255」になってしまい白くなった。。。

こんなところでしょうか?ほんとは元のイメージに落書きのイメージが表示されると思ったのですが。。。

次回は、「引き算」をやります。

Java OpenCV Lv8 〜ROIについて〜

イントロダクション

前回やった、Ellipseメソッドは今後触れることにします。下の本を読むとまだ出てこないみたいです。そんなわけで!やっとこの本に戻ってきました。

ROIについて

画像処理をするときに焦点を絞って処理する機能のようです。

兎にも角にも動かさないと始まらないので。。。

参考サイト:OpenCV Basics

参考にしたコード(ページ) (Stack Overfllow)

<表示するイメージファイル>

<実行結果1「単純にイメージファイルを表示>

<ソース1>

/**
 * メインメソッド、書き方は決まっている。
 * イメージファイルを読み込んで線を引く
 * 
 * @param argsプログラム引数
 */
public static void main(String[] args) {
	long start = System.currentTimeMillis();
	// 100x100の白いPNGからのデータを作成する
	Mat src = Imgcodecs.imread(OpenCVTest8.class.getResource("/images/mountain.png").getPath());

	// 自作のJFrame拡張クラス
	ViewFrame frame = new ViewFrame(new Mat(src, roi));
	System.out.println("実行時間: " + (System.currentTimeMillis() - start) + "ミリ秒");
}

ViewFremeクラスはGitを参照ください。

ぱっと見では「Macのデスクトップなんぢゃね?」となりますが、ちゃんとSwingしてます。上の部分に画面タイトル「Show Image」が表示してあります。

ついでに下の部分に「実行時間: 847ミリ秒」と表示しています。

ここからが勝負

まずは、トリミングをして見ます。表示するイメージファイルは「798x503」のサイズなので、座標(100,100)を指定して幅と高さも100にしています。

<実行結果:トリミングしてみる>

サイズが100x100なので小さくなっています。ちなみに処理時間は200ミリ秒ほど短縮しています。

<ソース2>

/**
 * メインメソッド、書き方は決まっている。
 * イメージファイルを読み込んで線を引く
 * 
 * @param argsプログラム引数
 */
public static void main(String[] args) {
	long start = System.currentTimeMillis();
	// 100x100の白いPNGからのデータを作成する
	Mat src = Imgcodecs.imread(OpenCVTest8.class.getResource("/images/mountain.png").getPath());
        /* トリミングを行なってみる
	 * x座標:100, y座標:100, 幅: 100, 高さ:100
	 */
	Rect roi = new Rect(100, 100, 100, 100);
	// 自作のJFrame拡張クラス
	ViewFrame frame = new ViewFrame(new Mat(src, roi));
	System.out.println("実行時間: " + (System.currentTimeMillis() - start) + "ミリ秒");
}

赤い字の部分が<ソース1>と違う部分です。

本日の目玉!

画像の部分修正です。今回は画面の左上の方に白い四角を描きました。

見辛いので色をつけましたが。。。イマイチ!


ソース3> ※Gitからソースをダウンロードできます。

/**
 * メインメソッド、書き方は決まっている。
 * イメージファイルを読み込んで線を引く
 * 
 * @param argsプログラム引数
 */
public static void main(String[] args) {
	long start = System.currentTimeMillis();
	// 100x100の白いPNGからのデータを作成する
	Mat src = Imgcodecs.imread(OpenCVTest8.class.getResource("/images/mountain.png").getPath());
	/* トリミングを行なってみる
	 * x座標:100, y座標:100, 幅: 100, 高さ:100
	 */
	Rect rect = new Rect(100, 100, 100, 100);
	// 領域を指定、白の資格を描く
	Imgproc.rectangle(src, new Point(100, 100), new Point(200, 200), new Scalar(255, 255, 255));
	ViewFrame frame = new ViewFrame(src);
	System.out.println("実行時間: " + (System.currentTimeMillis() - start) + "ミリ秒");
		
	}

関連ページ一覧

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java OpenCv Lv1 〜入門: 写真の表示〜
  3. Java OpenCV Lv2 〜画像を表示する〜
  4. Java OpenCV Lv3 〜画像の平滑化(smooth())〜
  5. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())〜
  6. Java OpenCV Lv4 〜画像の中身をみてみる〜
  7. Java OpenCV Lv5 〜Matクラスで描画処理〜
  8. Java OpenCV Lv6 〜Matクラスで背景から作成してみる〜
  9. Java OpenCV Lv7 〜MatクラスでEllipseしてみる〜
  10. Java OpenCV Lv9 〜画像編集「足し算」(cvAdd)〜
  11. Java OpenCV Lv9 〜画像編集「引き算」(cvSubtract)〜
  12. Java OpenCV Lv9 〜画像の掛け算〜
  13. Java OpenCV Lv10 〜行列演算Mat#submat()〜
  14. Java OpenCv Lv10〜画像の平均値をだす〜

Java OpenCV Lv7 〜MatクラスでEllipseしてみる〜

イントロダクション

引き続き、OpenCVをいじり倒していきます。今度は「Ellipse」メソッドを使用して見ます。黒い背景のイメージを読み込んで単純にメソッドを使用して見ます。

<実行結果1>

ソース> ※Gitからダウンロードできます。

/**
 * メインメソッド、書き方は決まっている。
 * イメージファイルを読み込んで線を引く
 * 
 * @param argsプログラム引数
 */
public static void main(String[] args) {
	// 100x100の白いPNGからのデータを作成する
	Mat src = Imgcodecs.imread(OpenCVTest7.class.getResource("/images/black.png").getPath());
	Imgproc.ellipse(src
			, new Point(src.width() /2, src.height() / 2)
			, new Size(src.width() / 4, src.width() / 16)
			, 0     // angle
			, 0.0   // ??
			, 360.0 // 0と360度の間の弧を延長する
			, new Scalar(255, 0, 0) // BGRの値
			, 2   // thickness
			, 8   // lineType
			, 0); // Shift
	System.out.println(src.dump());
	// 自作のJFrame拡張クラス
	new ViewFrame(src);
}

よくわからないけど真ん中に輪っかができました。

今度はImgproc#ellipse()のパラメータを変更して実行します。

「new Size(src.width() / 4, src.width() / 4)」に変更します。

<実行結果2>

普通の丸になりました。今度は逆にして見ます。

new Size(src.width() / 16, src.width() / 4)

<実行結果3>

縦長になりました。第三引数のサイズの第一引数がX軸方向の幅、第二引数がY軸方向の幅を定義しているので「丸」が開店しているようなイメージになるのであろうと推測します。なんか推測ばかりで確証までなかなかたどり着きませんが。。。次は連続してラベルを表示して見ます。その際にEllipseの値を変更して追加します。

	/**
	 * メインメソッド、書き方は決まっている。
	 * イメージファイルを読み込んで線を引く
	 * 
	 * @param argsプログラム引数
	 */
	public static void main(String[] args) {
		// 100x100の白いPNGからのデータを作成する
		Mat src = Imgcodecs.imread(OpenCVTest7.class.getResource("/images/black.png").getPath());
		src = ellipseLabel(src, 4, 16);
		System.out.println(src.dump());
		// 自作のJFrame拡張クラス
		ViewFrame frame = new ViewFrame(src);
		sleepFrame();
		frame.updateLabel(ellipseLabel(src, 16, 4));
		sleepFrame();
		frame.updateLabel(ellipseLabel(src, 2, 4));
	}

	private static Mat ellipseLabel(Mat src, int left, int right) {
		Imgproc.ellipse(src
				, new Point(src.width() /2, src.height() / 2)
				, new Size(src.width() / left, src.width() / right)
				, 0     // angle
				, 0.0   // ??
				, 360.0 // 0と360度の間の弧を延長する
				, new Scalar(255, 0, 0) // BGRの値
				, 2   // thickness
				, 8   // lineType
				, 0); // Shift
		return src;
	}

	private static void sleepFrame() {
		try {
			Thread.sleep(3000L);
		} catch(Exception e) {
			e.printStackTrace();
			System.exit(-1);
		}

	}

 

	/** 引っ越し2018/11/23 */
	private final JLabel label;
	
	public ViewFrame(Mat image) {
		// 受け取ったMatを表示する
		super("Show Image");
		// JFrameのメソッドを呼び出す(親クラス)
		Container con = super.getContentPane();
		
		// 入力ストリームを用意する
		BufferedImage buf = getIconImage(image);
		// ラベルをフィールドに持っていく 2018/11/23
		label = new JLabel(new ImageIcon(buf));
		con.add(label);
		// JFrameを閉じるときにアプリケーションも終わるようにする
		super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// コンポーンエントの配置を整理する
		super.pack();
		// JFrameを表示する(これをやらないと何も表示されない)
		setVisible(true);
	}

	private BufferedImage getIconImage(Mat image) {
		// イメージのバイト配列を受ける変数
		MatOfByte bytes = new MatOfByte();
		// 上の変数にpngファイルを書き込む
		Imgcodecs.imencode(".png", image, bytes);
		// プリミティブ型に変換
		byte[] b = bytes.toArray();
		// 入力ストリームを用意する
		InputStream in = new ByteArrayInputStream(b);
		
		BufferedImage buf = null;
		try {
			// イメージとして読み込む
			buf = ImageIO.read(in);
		} catch(IOException e) {
			e.printStackTrace();
		}
		return buf;
	}
	/**
	 * 引数のMat(イメージ)でラベルを更新します。
	 * @param img Mat
	 */
	public void updateLabel(Mat img) {
		label.setIcon(new ImageIcon(getIconImage(img)));
	}

<実行結果4>


今回はここまでにしておきます。

関連ページ一覧

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java Install Eclipse〜開発ツールのインストール〜
  3. Java OpenCv Hello in Java〜OpenCv事始め〜

OpenCv

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java OpenCv Lv1 〜入門: 写真の表示〜
  3. Java OpenCV Lv2 〜画像を表示する〜
  4. Java OpenCV Lv3 〜画像の平滑化(smooth())〜
  5. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())〜
  6. Java OpenCV Lv4 〜画像の中身をみてみる〜
  7. Java OpenCV Lv5 〜Matクラスで描画処理〜
  8. Java OpenCV Lv6 〜Matクラスで背景から作成してみる〜
  9. Java OpenCV Lv7 〜MatクラスでEllipseしてみる〜
  10. Java OpenCV Lv9 〜画像編集「足し算」(cvAdd)〜
  11. Java OpenCV Lv9 〜画像編集「引き算」(cvSubtract)〜
  12. Java OpenCV Lv9 〜画像の掛け算〜
  13. Java OpenCV Lv10 〜行列演算Mat#submat()〜
  14. Java OpenCv Lv10〜画像の平均値をだす〜

 

Java OpenCV Lv6 〜Matクラスで背景から作成してみる〜

イントロダクション

前回はMatクラスを使用して描画処理を行って見ました。ただし、背景のイメージファイルを読み込んで起動、一からのイメージ作成はしていませんでした。

今回は、Matクラスでイチからイメージを作成してみようと思います。

<今までの記事>

  1. ava OpenCV 環境セットアップ(on Mac)
  2. Java OpenCv Lv1 〜入門: 写真の表示〜
  3. Java OpenCV Lv2 〜画像を表示する〜
  4. Java OpenCV Lv3 〜画像の平滑化(smooth())〜
  5. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())〜
  6. Java OpenCV Lv4 〜画像の中身をみてみる〜
  7. Java OpenCV Lv5 〜Matクラスで描画処理〜

Gitからのプロジェクト取得


やることはすごくシンプル

Matクラスをnewするときにちゃんとパラメータを渡してあげるだけです。

引数に画像のサイズ(int, int)「CvType.CV_8UC1」を渡してやるだけです。そして左のリンクページの記事より、扱えるチャネル(Channel)は1つということで。。。

色は黒と白のみなのであろう(予想)と思いました。

<実行結果1:CV_8UC1>

<ソース>

public static void main(String[] args) {
	// 100x100の白いPNGからのデータを作成する
//	Mat src = Imgcodecs.imread(OpenCVTest6.class.getResource("/images/black.png").getPath());
	Mat src = new Mat(50, 50, CvType.CV_8UC1);
	Point pt1 = new Point(0,0);
	Point pt2 = new Point(50,50);
	Imgproc.line(src, pt1, pt2, new Scalar(240, 255, 240), 1);
	System.out.println(src.dump());
	// 自作のJFrame拡張クラス
	new ViewFrame(src);
}

ちなみに白(255)で試してもゴミ(ノイズ?)が出ていました。

これは指定した色、他のプロパティ(Channelなど)が「CV_8UC1」に対応していないためと予想します。そんなわけで、CV_8UC2で起動しました。

エラーが出て以下の文言が見当たりました。

channels == 1 || channels == 3 || channels == 4 in function 'imencode'

どうやらチャネル数は1,3,4のどれかでなくてはならないようです。

改めて「CV_8UC3」で勝負!

<実行結果2:CV_8UC3>

動いたけど、ゴミはまだ残っています。そして、実行結果1と比べて少しカラフルになりました。ついでなのでCV_8UC4も実行して見ます。

<実行結果4>

おお!背景の「黒」が消えた!!ついでだから他のものも。。。

【補足】上記の「ソース」の赤い字の部分のみを変更して実行しています。

<実行結果5:CV_8SC1>

<実行結果6:CV_8SC3>

<実行結果7:CV_8SC4>

ここまでやったら、XX4は黒が透明になルようだと推定します。

<実行結果8:CV_16UC3>

おお!綺麗に線が引けた感じがする。。データは下のようになりました。

理由はよくわからないが、8XXだとゴミが入るけど16XXの時はゴミが入らなそうだ。

関連ページ一覧

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java Install Eclipse〜開発ツールのインストール〜
  3. Java OpenCv Hello in Java〜OpenCv事始め〜

Java OpenCV Lv5 〜Matクラスで描画処理〜

イントロダクション

今までの学習で、OpenCV(JavaCV)でフィルター処理(①)と描画処理(②)ができることがわかりました。

  1. Java OpenCV Lv2 〜画像を表示する〜 シンプルに画像を表示する
  2. Java OpenCV Lv3 〜画像の平滑化(smooth())〜 ①
  3. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())〜 ①
  4. Java OpenCV Lv3 〜画像に中央値フィルタ(medianBlur())〜 ①
  5. Java OpenCV Lv4 〜画像の中身をみてみる〜 ②

Gitからのプロジェクト取得


イメージファイル(PNG, JPGなど)の中身は数値型の配列になっていることがわかったので、次はそいつらをコントロールする方法を学びます。

<参考サイト>

MatクラスのJavaDoc

OpenCVのチュートリアル

RBGの値と色の表

学習ポイント

  1. 数値型の配列([0, 255, 255, 0, ...])の値はRGBが割り当てられていることを確認
  2. Matクラスでの描画方法(線を引く)

早速実装に入ります。

注意点として、白はデータ上「255」になります。なので白い背景を使用したら全部が「255」になります。

そして、実装するときには以下の手順でやります。

  1. OpenCVのライブラリを読み込む(staticで読み込みます)
  2. white100x100.png(背景)をロードする
  3. 線の開始点、終点を指定する(Point)
  4. Imgproc.line()で描画する

描画メソッドの参考

<実行結果>

<ソース(OpenCVTest5.java>

/**
 * メインメソッド、書き方は決まっている。
 * イメージファイルを読み込んで線を引く
 * 
 * @param argsプログラム引数
 */
public static void main(String[] args) {
	// 100x100の白いPNGからのデータを作成する
		Mat src = Imgcodecs.imread(OpenCVTest5.class.getResource("/images/white100x100.png").getPath());
	Point pt1 = new Point(0,0);
	Point pt2 = new Point(100,100);
	Imgproc.line(src, pt1, pt2, new Scalar(240, 255, 240), 4);

	System.out.println(src.dump());
	// 自作のJFrame拡張クラス
	new ViewFrame(src);
}

Imageデータを編集(描画)してから、表示に使用しているViewFrameクラスはjavax.swingパッケージ(Swing)を使用しています。

<自分で試す手順>

  1. Eclipseをセットアップする
  2. 関連ページ一覧のOpenCV(JavaCV)のセットアップを行う
  3. Gitから作成したソース、リソースをダウンロード
  4. Eclipseでプログラムを実行する

関連ページ一覧に補足になるような記事があります。

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseにCDTをインストール〜
  3. Setup OpenGL with Java〜JOGLを使う準備 for Eclipse〜
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)

OpenCv

  1. Java OpenCV 環境セットアップ(on Mac)
  2. Java OpenCv Lv1 〜入門: 写真の表示〜
  3. Java OpenCV Lv2 〜画像を表示する〜
  4. Java OpenCV Lv3 〜画像の平滑化(smooth())〜
  5. Java OpenCV Lv3 〜画像にガウシアンフィルタ(GaussianBlur())〜
  6. Java OpenCV Lv3 〜画像に中央値フィルタ(medianBlur())〜
  7. Java OpenCV Lv4 〜画像の中身をみてみる〜
  8. Java OpenCV Lv5 〜Matクラスで描画処理〜
  9. Java OpenCV Lv6 〜Matクラスで背景から作成してみる〜
  10. Java OpenCV Lv7 〜MatクラスでEllipseしてみる〜
  11. Java OpenCV Lv9 〜画像編集「足し算」(cvAdd)〜
  12. Java OpenCV Lv9 〜画像編集「引き算」(cvSubtract)〜
  13. Java OpenCV Lv9 〜画像の掛け算〜
  14. Java OpenCV Lv10 〜行列演算Mat#submat()〜
  15. Java OpenCv Lv10〜画像の平均値をだす〜