C言語_HSV値_ヒストグラム

ゆうき( @BASEBALLY15 )です^^

今日はどうしたんじゃ?

C言語を使って,画像内の特定のHSV値をヒストグラムで表示したいのですが,どのようにコードを書けばよいのでしょうか?

そうじゃのう.
これから紹介するコードを書けばいいぞい!
(※Visual Studio 2017 を使用)

分かりました!

HSV値って何?

HSVとは,色相(Hue)彩度(Saturation・Chroma)明度(Value・Brightness)の3つの成分から成る色空間のことを言います.

HSV
(画像をクリックすると,引用元を確認できます.)
  • 色相(H)→具体的な色を定義する要素.0°~360°で表現される.
  • 彩度(S)→色の鮮やかさ濃さを表す要素.0%~100%で表現される.
  • 明度(V)→色の明るさ暗さを表す要素.0%~100%で表現される.

といったように,色を定義するだけでなく,

どのような特徴を持った色なのか

ということまで,表現することができます.

具体的に何が出来るの?

今回ご紹介するコードを書くことで,カラー画像の指定した箇所のHSV値をヒストグラムで表示することが出来ます.

上の画像の『YProg』と書かれた箇所のHSV値をヒストグラムで表示したものが上の3つのブラフです.

それでは,画像内の特定のHSV値をヒストグラムで表示する方法を見てきましょう⤵

C言語で画像内の特定のHSV値をヒストグラムで表示しよう!

最初に手順を載せておきます.

  1. 『Paint.NET』というソフトでモノクロ画像を作る.
  2. カラー画像とモノクロ画像を『RAW形式』に変換する.(XnViewを使用)
  3. 『gnuplot』をダウンロード
  4. コードを書いていく
  5. 実際に画像内のHSV値をヒストグラムで表示する.

1. 『Paint.net』でモノクロ画像を作ろう!

(画像をクリックすると,ダウンロードページへ移動します,)

まずは,『Paint.NET』というソフトを使って,モノクロ画像を作ります.

Paint.NETを開き,HSV値を調べたい画像を読み込ませます.

今回は,画像内の『YProg』と書かれている個所のHSV値をヒストグラムで表示します.

ツール』の『鉛筆』を使って(上図),YProgという文字の外枠をなぞります(下図).

そして,『ペイント缶』を使って,YProgの文字の外側を黒色で塗りつぶします.

ペイント缶』で上手く塗りつぶせない箇所は,『ペイントブラシ』を使うことで,綺麗に塗りつぶすことが出来ます.

(※『ツール』の横の『ブラシ幅』を変更することで,ブラシの大きさを変えることが出来ます.)

同じ要領で『YProg』という文字は白色で塗りつぶします.

2. 画像をRAW形式に変換しよう!(Xn.Viewを使用)

画像の形式と言えば,『.jpeg』や『.png』を思い浮かべるのではないでしょうか?

実はこの形式のままでは,画像処理を行うことが出来ません.

画像処理を行うためには,画像をRAW形式に変換しておく必要があります.

以下のサイトで,RAW形式に変換する方法をご紹介しているので,先にそちらを見て頂いた方がいいかもしれません^^

標準入力関数を使えるようにしよう!

Visual Studio 2012以降』を使っている方は,標準入力を行うための関数を使うために,少し処理を行わなければいけません.

今から,その方法をご紹介します.

Visual Studioのヘッダーファイルを開いてください.

そして,『pch.h』をダブルクリックして,以下のコードを記述してください.

#define _CRT_SECURE_NO_WARNINGS

これで,標準入力を行うための関数(scanfやfopenなど)を使うことが出来ます^^

3. gnuplotをダウンロードしよう!

ヒストグラムで表示する際に,『gnuplot』というグラフ作成ソフトを使います.

以下の記事で,gnuplotのダウンロード方法を紹介しているので,先にそちらを確認して頂くことをオススメします!

4. C言語でコードを書いていこう!

#include "pch.h"
#include "stdio.h"
#include "stdlib.h"

//--------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------

//画像サイズを決める
#define X_SIZE 640
#define Y_SIZE 480

//1枚目の画像(カラー画像)を読み込むファイルの配列数を決める
char filepath[500];
//2枚目の画像(Paint.NETで作成)を読み込むファイルの配列数を決める
char second_filepath[500];

//rgb値の配列を宣言する
unsigned char r[Y_SIZE][X_SIZE], g[Y_SIZE][X_SIZE], b[Y_SIZE][X_SIZE];
//paint.netで作成した画像のrgb値の配列を宣言する
unsigned char r_in_paint[Y_SIZE][X_SIZE], g_in_paint[Y_SIZE][X_SIZE], b_in_paint[Y_SIZE][X_SIZE];
//ヒストグラムの結果用の配列を宣言する
long Hist[400];

//--------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------

//プロトタイプ宣言(作成した関数を始めに読み込む)
//rgb画像を読み込むための関数(RGB値それぞれを取得)
void Read_rgbimage(char *Read_filepath, unsigned char r[Y_SIZE][X_SIZE], unsigned char g[Y_SIZE][X_SIZE], unsigned char b[Y_SIZE][X_SIZE]);
//画像を切り出して,その範囲のHSV値をヒストグラムに表す(Paint.NETを使用)
void paint_dot_NET(unsigned char r_in[Y_SIZE][X_SIZE], unsigned char g_in[Y_SIZE][X_SIZE], unsigned char b_in[Y_SIZE][X_SIZE], unsigned char r_paint[Y_SIZE][X_SIZE], unsigned char g_paint[Y_SIZE][X_SIZE], unsigned char b_paint[Y_SIZE][X_SIZE], long hist[400]);
//gnuplotを表示
void gnuplot(long hist[400]);

//--------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------

//メインとなる関数
void main(void)
{
	//1枚目の画像を読み込む
	printf("読み込む画像ファイル1枚目=");
	scanf("%s", filepath);
	
	//1枚目の画像のrgb画像を読み込むための関数を呼び出す
	Read_rgbimage(filepath, r, g, b);
	
	//2枚目の画像を読み込む
	printf("読み込む画像ファイル2枚目(paint.net)=");
	scanf("%s", second_filepath);
	
	//2枚目の画像のrgb画像を読み込むための関数を呼び出す
	Read_rgbimage(second_filepath, r_in_paint, g_in_paint, b_in_paint);
	
	//ヒストグラムに表す
	paint_dot_NET(r, g, b, r_in_paint, g_in_paint, b_in_paint, Hist);
	
	//gnuplotを表示
	gnuplot(Hist);
}

//--------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------

//RGBカラー画像を読み込むための関数(RGB値それぞれを取得)
void Read_rgbimage(char *Read_filepath, unsigned char r[Y_SIZE][X_SIZE], unsigned char g[Y_SIZE][X_SIZE], unsigned char b[Y_SIZE][X_SIZE])
{
	//変数を定義
	int i, j;

	//画像を読み込むための準備
	FILE *r_filepath;
	r_filepath = fopen(Read_filepath, "rb");
	//もし,r_filepathが無かったら,「存在しません」と表示する
	if (r_filepath == NULL)
	{
		printf("%sは存在しません!", Read_filepath);
		exit(-1);
	}

	//rgb値を読み込む
	for (j = 0; j < Y_SIZE; j++)
	{
		for (i = 0; i < X_SIZE; i++)
		{
			//画像のRGB値を取得
			r[j][i] = fgetc(r_filepath);
			g[j][i] = fgetc(r_filepath);
			b[j][i] = fgetc(r_filepath);
		}
	}

	//画像ファイルを閉じる
	fclose(r_filepath);
}

//--------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------

//画像を切り出して,その範囲のHSV値をヒストグラムに表す(Paint.NETを使用)(2枚の画像を読み込む)
void paint_dot_NET(unsigned char r_in[Y_SIZE][X_SIZE], unsigned char g_in[Y_SIZE][X_SIZE], unsigned char b_in[Y_SIZE][X_SIZE], unsigned char r_paint[Y_SIZE][X_SIZE], unsigned char g_paint[Y_SIZE][X_SIZE], unsigned char b_paint[Y_SIZE][X_SIZE],long hist[400])
{
	int i, j, n;

	//RGB値からHSV値に変換する
	double max, min;
	double R, G, B;

	//ヒストグラムの値が入る箱内を初期化
	for (n = 0; n < 400; n++)
	{
		hist[n] = 0;
	}

	for (j = 0; j < Y_SIZE; j++)
	{
		for (i = 0; i < X_SIZE; i++)
		{
			//1枚目の出力用のrgb値を用意
			r_out[j][i] = r_in[j][i];
			g_out[j][i] = g_in[j][i];
			b_out[j][i] = b_in[j][i];

			//2枚目(Paint.NETで作成した画像)
			//出力用のrgb値を用意
			r_out_paint[j][i] = r_in_paint[j][i];
			g_out_paint[j][i] = g_in_paint[j][i];
			b_out_paint[j][i] = b_in_paint[j][i];

			//カラー画像のRGB値をHSV値に変換する(131行~210行)
			R = (double)r_out[j][i] / 255.0;
			G = (double)g_out[j][i] / 255.0;
			B = (double)b_out[j][i] / 255.0;

			//最大値・最小値
			if (R >= G && R >= B)
			{
				max = R;

				if (G < B)
				{
					min = G;
				}
				else
				{
					min = B;
				}
			}
			else if (G >= R && G >= B)
			{
				max = G;

				if (R < B)
				{
					min = R;
				}
				else
				{
					min = B;
				}
			}
			else
			{
				max = B;

				if (R < G)
				{
					min = R;
				}
				else
				{
					min = G;
				}
			}

			//Hue(色相)
			if (max == min)
			{
				h_out = 0.0;
			}

			else if (max == R)
			{
				h_out = 60.0 * (G - B) / (max - min);
			}
			else if (max == G)
			{
				h_out = 60.0 * (B - R) / (max - min) + 120.0;
			}
			else if (max == B)
			{
				h_out = 60.0 * (R - G) / (max - min) + 240.0;
			}

			if (h_out < 0.0)
			{
				h_out += 360.0;
			}

			//Saturation(彩度)
			if (max == 0.0)
			{
				s_out = 0.0;
			}
			else
			{
				s_out = ((max - min)*100.0) / max;
			}

			v_out = max * 100.0;

			//もし,paint.netの画像のRGB値が全て127以上(白と黒の確実な分かれ目)なら・・・
			if ((r_out_paint[j][i] >= 127) && (g_out_paint[j][i] >= 127) && (b_out_paint[j][i] >= 127))
			{
				//hsv値をヒストグラムに代入していく(h_out,s_out,v_outを都度書き替える)
				n = int(h_out);
				//n = int(s_out);
				//n = int(v_out);
				hist[n]++;
			}
		}
	}
}

//--------------------------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------------------

//gnuplot用の関数
void gnuplot(long hist[400])
{
	int c;

	//gnuplotを呼びだすために記述
	FILE *gid;

	//gnuplotを呼びだす
	gid = _popen("c:/ooooooooo/xxxxxxxxx/bin/gnuplot.exe", "w");

	//x軸の範囲を指定
	fprintf(gid, "set xrange[0:100]\n");

	//ラベル名(H,S,Vで都度変更する)
	fprintf(gid, "set xlabel \"H\n");
	//fprintf(gid, "set xlabel \"S\n");
	//fprintf(gid, "set xlabel \"V\n");
	fprintf(gid, "set ylabel\"Number of pixels\n");

	//凡例を非表示
	fprintf(gid, "unset key\n");

	fprintf(gid, "plot'-' with boxes \n");

	//H値の時・・・
	for (c = 1; c < 360; c++)
	{
		//gnuplotに書き込んでいく
		fprintf(gid, "%d\t%d\n", c, hist[c]);
	}

	//SとV値の時・・・
	/*
	for (c = 1; c < 100; c++)
	{
		//gnuplotに書き込んでいく
		fprintf(gid, "%d\t%d\n", c, hist[c]);
	}
	*/


	//guplotの後処理
	fprintf(gid, "e\n");
	fflush(gid);
	fprintf(gid, "pause mouse\n");
	fflush(gid);
	_pclose(gid);

}

5. 実際にヒストグラムを表示してみよう!

コードを書き終えたら,『Ctrl+F5』で実行します.

そして,1枚目の画像ファイルにはカラー画像を,2枚目の画像ファイルにはモノクロ画像を読み込ませます.

すると,下の画像の様に,HSV値をヒストグラムで表示することが出来ます⤵

終わりに

今回は,C言語を使って画像内の特定のHSV値をヒストグラムで表示する方法についてご紹介しました.

今回のコードを少し書き換えると,RGB値をヒストグラムで表示することも出来ます.

今後は,ヒストグラムで表示したHSV値から画像内の特定の箇所を抽出する方法をご紹介します.

それでは・・・

Q &A

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA