之前有寫過一篇關於OpenCV像素取/存值的方式[1]
彩色像素取值
image.at<cv::Vec3b>(row, col)[0]
image.at<cv::Vec3b>(row, col)[1]
image.at<cv::Vec3b>(row, col)[2]
灰階像素取值
image.at<uchar>(row, col) = 255;
加上另一篇[2] 利用C# 拉UI並傳參數給OpenCV C++ DLL
規劃如下:
C# 公開函數:
UI只需要傳入雜訊點數(numPts),
對使用者來說並不需要知道用哪一個Mat影像資料(這部分由C++實作)
C++私有函數:
針對圖像資料(image), 加入指定的雜訊點數(numPts),
該點數則C#公開函數傳來
宣告imageProcLib.hpp
實作imageProcLib.cpp
//----------------------------------------------------------------
void ImageProc::AddNoise(int numPts)
{
imgDst = img.clone();
AddNoise(imgDst, numPts);
}
void ImageProc::AddNoise(Mat image, int numPts)
{
int i, row, col;
if(image.channels() == 3) // 彩色
{
for(i=0; i<numPts; i++)
{
row = rand() % image.rows;
col = rand() % image.cols;
image.at<cv::Vec3b>(row, col)[0] = 255;
image.at<cv::Vec3b>(row, col)[1] = 255;
image.at<cv::Vec3b>(row, col)[2] = 255;
}
}else // 灰階
{
for(i=0; i<numPts; i++)
{
row = rand() % image.rows;
col = rand() % image.cols;
image.at<uchar>(row, col) = 255;
}
}
}
//----------------------------------------------------------------
=====================================================================
C# GUI如下
為了怕使用者未先載入圖像所以加入讀取檔案是否成功
imageProcLib.hpp
bool ImageProc::IsImgLoaded(); // 檢查讀取檔案是否成功
imageProcLib.cpp
bool ImageProc::IsImgLoaded() // 檢查讀取檔案是否成功
{
return (!img.data);
}
C# UI選取一張照片, 然後傳入雜訊點數並顯示結果影像
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
unsafe
{
fixed (char* filename = ofd.FileName)
{
imgProc.imread(filename);
int numPts = Convert.ToInt16(numericUpDown1.Value);
if (!imgProc.IsImgLoaded()) // 檢查讀取檔案是否成功
{
imgProc.AddNoise(numPts); // 加入雜訊點數
imgProc.showImage(); // 顯示影像
}
}
}
}
}
}
// 動態更新
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int numPts = Convert.ToInt16(numericUpDown1.Value);
try
{
if (!imgProc.IsImgLoaded()) // 檢查讀取檔案是否成功
{
imgProc.AddNoise(numPts); // 加入雜訊點數
imgProc.showImage(); // 顯示影像
}
else
{
MessageBox.Show("請先載入一張照片");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
測試如下
===================================================================
接下來, 練習[3] normally distributed random numbers (randn)
以下為原發問者問題描述節錄:
here's my problem: I'm trying to create a simple program which adds Gaussian noise to an input image. The only constraints are that the input image is of type CV_64F (i.e. double) and the values are and must be kept normalized between 0 and 1.
The code I wrote is the following:
Mat my_noise;
my_ noise = Mat (input.size(), input.type());
randn(noise, 0, 5); //mean and variance
input += noise;
The above code doesn't work, the resulting image doesn't get displayed properly. I think that happens because it gets out of the 0,1 range. I modified the code like this:
Mat my_noise;
my_ noise = Mat (input.size(), input.type());
randn(noise, 0, 5); //mean and variance
input += noise;
normalize(input, input, 0.0, 1.0, CV_MINMAX, CV_64F);
but it still doesn't work. Again, the resulting image doesn't get displayed properly. Where is the problem? Remember: the input image is of type CV_64F and the values are normalized between 0 and 1 before adding noise and have to remain like also after the noise addition.
=====================================================================
imageProcLib.hpp
void AddNoiseRandN(); // 加入 normally distributed random numbers
void AddNoiseRandN(double mean, double std);
imageProcLib.cpp
//----------------------------------------------------------------
void ImageProc::AddNoiseRandN(double mean, double stddev)
{
if(img.channels() == 3)
{
vector<Mat> spl;
split(img, spl);
noise = Mat(spl[0].size(), CV_64F);
randn(noise, mean, stddev);
for(int i=0; i<3; i++)
{
normalize(spl[i], spl[i], 0.0, 1.0, CV_MINMAX, CV_64F);
spl[i] = spl[i] + noise;
}
merge(spl, imgDst);
}
}
void ImageProc::AddNoiseRandN() // 加入 normally distributed random numbers
{
AddNoiseRandN(0.0, 0.01);
}
C# UI
private void randNToolStripMenuItem_Click(object sender, EventArgs e)
{
double mean = Convert.ToDouble(textBox1.Text);
double stddev = Convert.ToDouble(textBox2.Text);
using (OpenFileDialog ofd = new OpenFileDialog())
{
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
unsafe
{
fixed (char* filename = ofd.FileName)
{
imgProc.imread(filename);
if (!imgProc.IsImgLoaded()) // 檢查讀取檔案是否成功
{
imgProc.AddNoiseRandN(mean, stddev);
imgProc.showImage(); // 顯示影像
}
}
}
}
}
}
參考資料
1. OpenCV 2.4.9 取得和設定彩色(灰階)影像的像素值
2. 利用C# 拉UI並傳參數給OpenCV C++ DLL
3. http://answers.opencv.org/question/36309/opencv-gaussian-noise/