画像処理、ノイズの除去
画像処理の勉強の為、画像処理の書籍資料を参考にして、実際にソフトウエアを作成し実験してみた。今回は画像に混入したノイズの除去を行う。
①9画素の平均化でノイズ除去(移動平均フィルタ)
以下の書籍を参考にさせていただいた。
・はじめてのデジタル画像処理 P49
・インターフェース2017/05 P58
下の図の様に元画像を計算して新しく作成した画像の画素の濃度を決める。
新しい画像の画素E'は、元画像Eとその周辺の画素との平均値から求める。
画像の素材はここからダウンロードさせていただいた。
これをグレースケールに変換した物が以下の画像。
このサイトにてノイズを混入させていただいた。
これを平均化処理をするソフトウエアを作成して加工を行った。
加工したのが以下の画像。濁った様な画像になってしまった。
以下が平均化処理行ったソフトウエアです。
C++で記述、画像を扱う為OpenCVを使用。
平均化処理は画像処理の勉強の為、資料を参考にして記述している。
ファイルのパスの指定は自分の環境のままですので、参考にされる場合は書き換えて下さい。
#include <opencv2/opencv.hpp>
int main()
{
// 加工前の画像読み込み
cv::Mat image = cv::imread("D:/PROG_WK/VisualStudio2022_WK/Project/100_OpenCV/11_NOISE_CUT_01/NOISE_GRY.jpg", cv::IMREAD_COLOR); // グレースケールファイルを用意カラーで読む
int x_max = image.cols;
int y_max = image.rows;
//空の画像生成
cv::Mat image2 = cv::Mat::zeros(cv::Size(x_max, y_max), CV_8UC3);
// 元画像の平均化して加工画像へ代入(9画素平均)
for (int y = 0; y_max > y; y++) { // y方向のループ
cv::Vec3b* dst = image2.ptr<cv::Vec3b>(y); // 加工画像y行目の先頭画素のポインタを取得
for (int x = 0; x_max > x; x++) { // x方向のループ
int cnt = 0, dt = 0;
for (int y0 = y - 1; y0 <= y + 1; y0++) {
for (int x0 = x - 1; x0 <= x + 1; x0++) {
if (0 <= y0 && y0 < y_max && 0 <= x0 && x0 < x_max) {
cv::Vec3b* src = image.ptr<cv::Vec3b>(y0);
cv::Vec3b pix = src[x0];
dt += pix[1]; cnt++;
}
}
}
int gray_dt = dt / cnt;
dst[x] = cv::Vec3b(gray_dt, gray_dt, gray_dt); // B, G, R
}
}
// 表示、保存
cv::imshow("", image2);
cv::imwrite("D:/PROG_WK/VisualStudio2022_WK/Project/100_OpenCV/11_NOISE_CUT_01/NOISE_CUT_01_GRY.jpg", image2); // JPEGフォーマットで保存
cv::waitKey(0); // グラフィック表示ウィンドウ上でキークリック
cv::destroyAllWindows();
}
②9画素の中央の値を選択してノイズ除去(メディアンフィルタ)
以下の書籍を参考にさせていただいた。
・はじめてのデジタル画像処理 P51
・インターフェース2017/05 P59
下の図の様に元画像を計算して新しく作成した画像の画素の濃度を決める。
新しい画像の画素E'は、元画像Eとその周辺の画素との全て参照して濃度順に並べて列の中央の値をE'に採用する。
画像2のノイズを混入した画像を中央値選択処理をするソフトウエアを作成して加工を行った。加工したのが以下の画像。ノイズは残ってしまった。
以下が中央値選択処理行ったソフトウエアです。
C++で記述、画像を扱う為OpenCVを使用。
中央値選択処理は画像処理の勉強の為、資料を参考にして記述している。
C++のソート機能を使っていますが、今回は、ソートの勉強では無い為使用した。
ファイルのパスの指定は自分の環境のままですので、参考にされる場合は書き換えて下さい。
#include <opencv2/opencv.hpp>
#include<algorithm> // sort()
int main()
{
// 加工前の画像読み込み
cv::Mat image = cv::imread("D:/PROG_WK/VisualStudio2022_WK/Project/100_OpenCV/12_NOISE_CUT_02/NOISE_GRY.jpg", cv::IMREAD_COLOR); // グレースケールファイルを用意カラーで読む
int x_max = image.cols;
int y_max = image.rows;
//空の画像生成
cv::Mat image2 = cv::Mat::zeros(cv::Size(x_max, y_max), CV_8UC3);
// 9画素の中央の値を選択してノイズ除去(メディアンフィルタ)
for (int y = 0; y_max > y; y++) { // y方向のループ
cv::Vec3b* dst = image2.ptr<cv::Vec3b>(y); // 加工画像y行目の先頭画素のポインタを取得
for (int x = 0; x_max > x; x++) { // x方向のループ
int cnt = 0;
int arr[9];
for (int y0 = y - 1; y0 <= y + 1; y0++) {
for (int x0 = x - 1; x0 <= x + 1; x0++) {
if (0 <= y0 && y0 < y_max && 0 <= x0 && x0 < x_max) {
cv::Vec3b* src = image.ptr<cv::Vec3b>(y0);
cv::Vec3b pix = src[x0];
arr[cnt++]= pix[1];
}
}
}
std::sort(arr, arr + cnt); // 配列のソート
int gray_dt = arr[cnt / 2 + cnt % 2]; // 中間くらいの値を読み取り
dst[x] = cv::Vec3b(gray_dt, gray_dt, gray_dt); // B, G, R
}
}
// 表示、保存
cv::imshow("", image2);
cv::imwrite("D:/PROG_WK/VisualStudio2022_WK/Project/100_OpenCV/12_NOISE_CUT_02/NOISE_CUT_02_GRY.jpg", image2); // JPEGフォーマットで保存
cv::waitKey(0); // グラフィック表示ウィンドウ上でキークリック
cv::destroyAllWindows();
}
今回は以上です。