【C++】グレースケール画像の二値化プログラム【画像処理の基本】

スポンサーリンク
homeimageC++
スポンサーリンク

はじめに

以前の記事で即席で作ったPGMファイルの二値化プログラムを載せました。

しかしながら、プログラムが汚く、制約が無駄に多いのが気になったので修正しました。

今回のプログラムでは前回のプログラムに比べて条件を二つ減らすことができました。

具体的には次のような画像について処理することができます。

  • PGMファイルである(テキストファイルP2)
  • 最大値は255まで

こちらでOpenCVを用いた二値化プログラムを公開しています。ライブラリを用いて良い場合には参考にしていただけると思います。

アルゴリズム

以前の記事では難しそうだと書いた画像ファイルを全て読み込んで配列に保存して、最後に閾値と比較して出力する。というプログラムになっています。

ヘッダー情報の処理

こちらについては以前の記事を参照してください。ほぼ同じです。

以前と異なるのは、書き込みを同時に行っていないため、サイズ情報および最大値を変数に格納して保存しているという点のみです。

画像情報の処理

  1. サイズ情報から得た数値の分だけ1から3を繰り返す
    1. ファイルから文字列の塊を一つ取り出す
    2. atoi関数で数値に変換
    3. 配列に順番に格納
  2. 配列から順番に数値を取り出して閾値と比較
    1. 数値の方が大きければ255を出力して改行
    2. 数値の方が小さい、もしくは同じであれば0を出力して改行

以前のものと比べると格段に単純かつ簡単な処理になっていますが、これで以前と同様のことができます。

ソースコード

#include <iostream>
#include <fstream>
#include <string>

std::string input = "input.pgm";
std::string output = "output.pgm";






//////////////////////////////////////////////////
//////////////////////////////////////////////////
//二値化処理
//////////////////////////////////////////////////
//////////////////////////////////////////////////

int do_binary(int tsd_v){ //二値化処理

	std::ifstream ifs(input); //入力ファイルストリーム
	
	std::string str_i; //文字列型
	
	int width = 0,height = 0; //画像サイズ格納用
	
	if(ifs.fail()){ //ファイルを開けない時のエラー処理
	
		std::cerr << "Failed to open file." << std::endl;
		
		return -1;
	}
	
	getline(ifs, str_i); //一行目の読み込み
	
	if(str_i != "P2"){ //P2画像でない時のエラー処理
		
		std::cerr << "Can not handle." << std::endl;
		
		return -1;
	}
	
	int max = 0;
	
	while(1){ //コメント行から最大値行までを読み取る.
		
		getline(ifs, str_i); //一行を読み取り
				
		if(str_i[0] != '#'){ //コメント行でなければループ処理を抜ける
		
			int sp_p = str_i.find_first_of(' ');
			
			std::string wid_str = str_i.substr(0,sp_p);
			std::string hei_str = str_i.substr(sp_p+1);
			
			width = atoi(wid_str.c_str()); //画像幅設定
			height = atoi(hei_str.c_str()); //画像高設定
			
			getline(ifs, str_i); //最大値行の読み込み
	
			max = atoi(str_i.c_str());
			
			if(max > 255){ //最大値が255より大きい時のエラー処理
				
				std::cerr << "Can not handle." << std::endl;
				return -1;
			}
			
			break;
		}
	}
	
	int data[width][height]; //データ格納用配列
	
	//データ格納
	for(int j = 0; j < height; j++){
		for(int i = 0; i < width; i++){
			
			ifs >> str_i;
			data[i][j] = atoi(str_i.c_str());
		}
	}
		
	//開いたファイルを閉じる
	ifs.close();
	
	std::ofstream ofs(output); //出力ファイルストリーム
	
	ofs << "P2" << std::endl; //画像種別の書き出し
	ofs << "#Made By Ktora" << std::endl; //コメントの書き出し
	ofs << width << ' ' << height << std::endl; //サイズの書き出し
	ofs << max << std::endl; //最大値の書き出し
	
	//閾値と比較して二値化
	for(int j = 0; j < height; j++){
		for(int i = 0; i < width; i++){
			
			if(data[i][j] > tsd_v){
				
				ofs << 255 << std::endl;
			}else{
				
				ofs << 0 << std::endl;
			}
		}
	}
	
	std::cout << "閾値:" << tsd_v << "\nで二値化しました." << std::endl;
	
	return 0;
}





//////////////////////////////////////////////////
//////////////////////////////////////////////////
//固定閾値法
//////////////////////////////////////////////////
//////////////////////////////////////////////////

int f_tsd(){ //固定閾値法

	int tsd_v; //閾値
	
	std::cout << "閾値を入力してください." << std::endl;
	std::cin >> tsd_v;

	return tsd_v; //閾値を返す
}





//////////////////////////////////////////////////
//////////////////////////////////////////////////
//メイン関数
//////////////////////////////////////////////////
//////////////////////////////////////////////////

int main(){
	
	do_binary(f_tsd());
	
	return 0;
}

実際に実行する。

今回も実際に実行して終わりにしたいと思います。

入力ファイルを次のグレースケール画像、閾値を120として実行する。

改正版二値化検証画像
元画像

得られた結果は次の通りです。

改正版二値化検証結果
実行結果

最後に

一応、コンパクトにはなったので良かったかなと思います。

ほかの閾値のプログラムについても、このプログラムと同様に簡単にできると思いますが、記事にするかは未定です。

気が向いたらやっていきたいと思います。

今回はこの辺で。

コメント

タイトルとURLをコピーしました