我的OpenCV学习笔记(三):利用操作像素完成简单的图像处理:加入椒盐噪声、图像翻转、改变对比度、图像锐化

时间:2021-02-26 20:30:42

在第二讲中,我介绍了如何操作每个像素,这次利用操作像素完成简单的图像处理操作。

首先从给图像加入椒盐噪声开始,椒盐噪声其实就是使图像的一些随机的像素为黑色(255)或者白色(0):

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;
void salt(Mat& image, int n)
{
for(int k=0; k<n; k++)
{
int i = rand()%image.cols;
int j = rand()%image.rows;

if(image.channels() == 1)
{
image.at<uchar>(j,i) = 255;
}
else
{
image.at<Vec3b>(j,i)[0] = 255;
image.at<Vec3b>(j,i)[1] = 255;
image.at<Vec3b>(j,i)[2] = 255;
}
}
}

在看主程序,主程序中通过中滤波来消除噪声的影响:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;
void salt(Mat&, int n=3000);
int main()
{
Mat image = imread("D:/picture/img.tif");
salt(image, 500);
cv::namedWindow("image");
cv::imshow("image",image);

//测试用滤波的手段消除椒盐噪声
Mat result;
Mat kernel;
int ddepth;
int kernel_size;
ddepth = -1;
int ind = 0;
while(true)
{
//每0.5秒刷新一次图像
int c = waitKey(500);
//按esc键退出程序
if((char)c == 27)
{
break;
}

kernel_size = 3+2*(ind%5);
kernel = Mat::ones(kernel_size,kernel_size,CV_32F)/(float)(kernel_size*kernel_size);
filter2D(image,result,ddepth,kernel);
imshow("滤波结果",result);
ind++;
}


waitKey(0);
}


可以看出,滤波器的核越大,对于噪声消除效果越好,但图像轮廓也越模糊。



 

图像的翻转:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
//声明一个变量来承载图片,大小为0*0
Mat image ;
cout<<"size:"<<image.size().height<<","<<image.size().width<<std::endl;
//读取一幅图像
image= imread("D:/picture/img.tif");
if (!image.data)
{
std::cout<<"read image fail!"<<std::endl;
}
//创建名为“My Image”的图像窗口
namedWindow("My Image");
//显示一幅图像
imshow("My Image",image);
//等待按键:返回值为按键的asc码值或者-1(当等待时间到了时,如果没有按键按下)
waitKey(0);
Mat result;
result.create(image.rows,image.cols,image.type());
int rows = image.rows;
int cols = image.cols;
//对图像做水平翻转
for(int i = 0;i < rows;i++)
{
for(int j = 0;j < cols;j++)
{
result.at<Vec3b>(i,j)[0] = image.at<Vec3b>(i,cols-j-1)[0];
result.at<Vec3b>(i,j)[1] = image.at<Vec3b>(i,cols-j-1)[1];
result.at<Vec3b>(i,j)[2] = image.at<Vec3b>(i,cols-j-1)[2];
}
}
//flip(image,result,1);//正值水平变换
//0垂直变换
//负值二者都有

namedWindow("output image");
imshow("output image",result);
waitKey(0);


return 0;
}


使用for循环完成的水平翻转效果与使用flip函数的效果相同。

接下来完成改变图像的对比度和亮度:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

//控制对比度
double alpha;
//控制亮度
int beta;

int main()
{
Mat image = imread("D:/picture/img.tif");
if(!image.data)
{
cout<<"fail to read a image"<<endl;
return -1;
}
Mat new_image = Mat::zeros(image.size(),image.type());
cout << " Basic Linear Transforms " << endl;
cout << "-------------------------" << endl;
cout << "* Enter the alpha value [1.0-3.0]: ";
cin>>alpha;
cout << "* Enter the beta value [0-100]: ";
cin>>beta;
for(int i = 0;i < image.rows;i++)
{
for(int j = 0;j < image.cols;j++)
{
for(int c = 0;c < 3;c++)
{
//由于运算结果可能不是整数,所以需要格式转换
new_image.at<Vec3b>(i,j)[c] = saturate_cast<uchar>(alpha*(image.at<Vec3b>(i,j)[c])+beta);
}
}
}
namedWindow("源图像");
imshow("源图像",image);
namedWindow("改变对比度和亮度的结果图像");
imshow("改变对比度和亮度的结果图像",new_image);
waitKey(0);
return 0;
}

最后是图像锐化:

#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
int main()
{
Mat image;
image = imread("D:/picture/img.tif",0);//读取的是图像的灰度值,所以在sharpen函数中没有考虑通道数
Mat result;
result.create(image.rows,image.cols,image.type());
//使用3*3滤波器,所以遍历的像素中不能包括图像最外围的一圈
for(int i = 1;i < image.rows-1;i++)
{
//前一行、当前行、后一行的指针
uchar* previous = image.ptr< uchar>(i-1);
uchar* current = image.ptr< uchar>(i);
uchar* next= image.ptr< uchar>(i+1);
//输出结果图像的行指针
uchar* output = result.ptr<uchar>(i);
for(int j = 1;j < image.cols - 1;j++)
{
//图像锐化操作
*output++= cv::saturate_cast<uchar>(5*current[j]-current[j-1]-current[j+1]-previous[j]-next[j]); //saturate_cast<uchar>会将小于0的置零,大于255的改为255
}
}
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows-1).setTo(cv::Scalar(0));
result.col(0).setTo(cv::Scalar(0));
result.col(result.cols-1).setTo(cv::Scalar(0));
/*
//调用滤波函数来完成图像的锐化
//滤波器的核
Mat kernel(3,3,CV_32F,Scalar(0));
// 分配像素置
kernel.at<float>(1,1) = 5.0;
kernel.at<float>(0,1) = -1.0;
kernel.at<float>(2,1) = -1.0;
kernel.at<float>(1,0) = -1.0;
kernel.at<float>(1,2) = -1.0;
//调用滤波函数
filter2D(image,result,image.depth(),kernel);
*/
imshow("源图像",image);
imshow("锐化结果",result);
waitKey(0);
return 0;
}

个人觉得,对于灰度图像的锐化程序都有点长了,对于彩色图像就更麻烦了,还是直接调用滤波函数完成图像锐化比较方便。