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

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

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

首先从给图像加入椒盐噪声开始,椒盐噪声其实就是使图像的一些随机的像素为黑色(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;
}

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