画像処理、濃度ヒストグラム表示
画像処理の勉強の為のソフトウエアの作成。今回は、画像の濃度ヒストグラムを表示するソフトウエアを作成する。
濃度ヒストグラムは、画像の画素ごとの濃度値の分布をグラフにして表示した物です。以下の書籍を参考にさせていただいた。
・はじめてのデジタル画像処理 P68 P69
・インターフェース2017/05 P36
輝度の濃度ヒストグラムを求める為、今回はグレースケールの画像を用意するのでは無く、まず元の画像をグレースケールに変換する。
書籍を参考にして以下の様に変換した。
Y(輝度) = 0.144 * B + 0.587 * G + 0.299 * R
各Y(輝度)の濃度(0~255)ごとの個数を保存する配列を用意して、
求めた輝度に対応した配列の要素をカウントアップしていく。
変換したグレースケール画像とY(輝度)の配列をグラフ化した物を表示させる。タイトルの画像が今回作成したプログラムを実行している様子。
処理前の画像。ここからダウンロードさせていただいた。
これを今回のプログラムでグレースケールに変換した物が以下の画像。
上記の画像の輝度の濃度ヒストグラム表示
以下がヒストグラム表示処理行ったソフトウエアです。
C++で記述、画像を扱う為OpenCVを使用。
ヒストグラム表示処理部分は資料のインターフェース掲載記事の計算方法と同じです。ファイルのパスの指定は自分の環境のままですので、参考にされる場合は書き換えて下さい。
#include <opencv2/opencv.hpp>
#include <iostream>
// ヒストグラム表示の仕様(定数)
#define H_X_MAX 256 // ヒストグラム要素数
#define H_Y_MAX 800 // ヒストグラムグラフの縦軸最大画素数
#define H_YOHAKU 20 // ヒストグラムグラフの余白(黒?)画素数
int main()
{
int h_dt[H_X_MAX], dt_max = 0;
// 加工前の画像読み込み
cv::Mat image = cv::imread("D:/PROG_WK/VisualStudio2022_WK/Project/100_OpenCV/32_Histogram_02/ORG_CLR_03.png", cv::IMREAD_COLOR); // カラーで読む
int x_max = image.cols;
int y_max = image.rows;
// 配列初期化
for (int i = 0; i < 256; i++) h_dt[i] = 0;
// ヒストグラム(histogram)データ収集
for (int y = 0; y < y_max; y++) { // y方向のループ
cv::Vec3b* src = image.ptr<cv::Vec3b>(y);
for (int x = 0; x < x_max; x++) { // x方向のループ
cv::Vec3b pix = src[x];
int gray_dt = 0.114 * pix[0] + 0.587 * pix[1] + 0.299 * pix[2]; // B, G, Rグレーに変換してデータの読み込み
if (gray_dt < 0) gray_dt = 0;
else if (255 < gray_dt) gray_dt = 255;
src[x] = cv::Vec3b(gray_dt, gray_dt, gray_dt); // B, G, R画像もグレーに変換
h_dt[gray_dt]++; // データ加算
if (h_dt[gray_dt] > dt_max) dt_max = h_dt[gray_dt]; // 最大値保存
}
}
// グレー画像表示保存
cv::imshow("GREY", image); // 画像表示、複数画面を表示する場合(""で囲まれた部分の)winnameの指定が必要
cv::imwrite("D:/PROG_WK/VisualStudio2022_WK/Project/100_OpenCV/32_Histogram_02/ORG_GRY_03.png", image); //
// ヒストグラム表示
// 空の画像生成
cv::Mat image2 = cv::Mat::zeros(cv::Size((H_X_MAX * 2) + (H_YOHAKU * 2), H_Y_MAX + (H_YOHAKU * 2)), CV_8UC3); // x, y
// X軸y軸の表示
cv::line(image2, cv::Point(H_YOHAKU, 0), cv::Point(H_YOHAKU, H_Y_MAX + (H_YOHAKU * 2) - 1), cv::Scalar(128, 128, 128), 2, cv::LINE_4); // y軸、グレー、太さ2、連結4(仮)
cv::line(image2, cv::Point(0, H_Y_MAX + H_YOHAKU), cv::Point(((H_X_MAX * 2) + (H_YOHAKU * 2) - 1), H_Y_MAX + H_YOHAKU), cv::Scalar(128, 128, 128), 2, cv::LINE_4); // x軸、グレー、太さ2、連結4(仮)
// 各要素
for (int i = 0; i < 256; i++) {
int x1, y1, x2, y2, temp;
x1 = x2 = i * 2 + H_YOHAKU;
temp = int(double(h_dt[i]) / double(dt_max) * double(H_Y_MAX)); // 掛け算、割り算部分のみ浮動小数点数で計算。(整数演算が混ざらない様に全てキャストするか演算順序を考えて記述。)
if (temp < 0) temp = 0;
else if (H_Y_MAX < temp) temp = H_Y_MAX;
y1 = H_Y_MAX - temp + H_YOHAKU;
y2 = H_Y_MAX + H_YOHAKU;
cv::line(image2, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(255, 255, 255), 2, cv::LINE_4); // 白、太さ2、連結4(仮)
}
// 表示、保存
cv::imshow("HISTOGRAM", image2); // 画像表示、複数画面を表示する場合(""で囲まれた部分の)winnameの指定が必要
cv::imwrite("D:/PROG_WK/VisualStudio2022_WK/Project/100_OpenCV/32_Histogram_02/Histogram_02.jpg", image2); // JPEGフォーマットで保存
// debug計算結果表示
std::cout<<"dt_max = "<<dt_max<<std::endl;
for (int i = 0; i < 256; i++) {
std::cout<<"h_dt["<<i<<"] = "<<h_dt[i]<<std::endl;
}
cv::waitKey(0); // グラフィック表示ウィンドウ上でキークリック
cv::destroyAllWindows();
}
今回は以上です。