Opencv对图像的直方图统计并实现对滤波方式的选择

Opencv对图像的直方图统计与滤波

基于Opencv实现在执行窗口可选滤波,要求如下:
设计一个软件,实现以下功能:
1.将图像拖入软件后,显示图像信息;
2.统计图像直方图并显示;
3.在监控台显示窗口输出菜单供选择滤波与增强方法,可供选择的方法包括:亮度/对比度调整、幂次变换、平滑滤波、高斯滤波、双边滤波,及按ESC或Q键结束程序;
4.按第2步选择的方法对输入图像进行图像变换或滤波,并可滑动块调节滤波参数及调节参数后的滤波结果,输出滤波图像与原图像的差异值图像;
5.计算滤波图像的MSE和PSNR;
6.将滤波图像的MSE和PSNR显示在图像的左上角;
7.按ESC后返回第1步。

代码如下

有点长,但其实很多块相似。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include<string.h>
using namespace cv;
using namespace std;

int g_slider_position1 = 20;
int g_slider_position2 = 0;
int g_slider_position3 = 0;
int g_slider_position4 = 0;
char str_mse[20], str_psnr[20];


Mat image;

void MSE_PSNR(Mat image, Mat output)                                      //计算MSE,PSNR
{
	int sum = 0;
	double mse, psnr;
	for (int i = 0; i < image.rows; i++)
	{
		for (int j = 0; j < image.cols; j++)
		{
			sum = sum + (image.at<uchar>(i, j) - output.at<uchar>(i, j)) * (image.at<uchar>(i, j) - output.at<uchar>(i, j));
		}
	}
	mse = sum / (image.rows * image.cols);
	psnr = 10 * log10(255 * 255 / mse);
	//以下为将数值字符串化
	memset(str_mse, 0, sizeof(str_mse));
	memset(str_psnr, 0, sizeof(str_psnr));
	sprintf_s(str_mse, "%.2f", mse);
	sprintf_s(str_psnr, "%.2f", psnr);

}

void callback_mici(int pos1, void*)                               //幂次变换
{

	double pos11, gamma;
	pos11 = (double)pos1;
	gamma = pos11 / 100;
	Mat image_output2, A, B;
	namedWindow("幂次变换", 0);
	image.convertTo(A, CV_32FC1, 1.0 / 255);
	pow(A, gamma, B);
	B.convertTo(image_output2, CV_8UC1, 255.0);
	MSE_PSNR(image, image_output2);
	putText(image_output2, str_mse, Point(100, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	putText(image_output2, str_psnr, Point(400, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	imshow("幂次变换", image_output2);
	waitKey(0);
}
void callback_gaosi(int pos2, void*)
{
	Mat image_gaosi;
	namedWindow("高斯滤波", 0);
	namedWindow("高斯滤波的差异值", 0);
	GaussianBlur(image, image_gaosi, Size(3, 3), pos2, 0);
	imshow("高斯滤波的差异值", image - image_gaosi);
	MSE_PSNR(image, image_gaosi);
	putText(image_gaosi, str_mse, Point(100, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	putText(image_gaosi, str_psnr, Point(400, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	imshow("高斯滤波", image_gaosi);
}
void callback_pinghua(int pos3, void*)
{
	Mat image_pinghua;
	namedWindow("平滑滤波", 0);
	namedWindow("平滑滤波的差异值", 0);
	boxFilter(image, image_pinghua, CV_8U, Size(pos3, pos3), Point(-1, -1), true, BORDER_DEFAULT);
	MSE_PSNR(image, image_pinghua);
	imshow("平滑滤波的差异值", image - image_pinghua);
	putText(image_pinghua, str_mse, Point(100, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	putText(image_pinghua, str_psnr, Point(400, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	imshow("平滑滤波", image_pinghua);
}
void callback_shuangbian(int pos4, void*)
{
	Mat image_shuangbian;
	namedWindow("双边滤波", 0);
	namedWindow("双边滤波的差异值", 0);
	bilateralFilter(image, image_shuangbian, pos4, 100, 100, BORDER_DEFAULT);
	imshow("双边滤波的差异值", image - image_shuangbian);
	MSE_PSNR(image, image_shuangbian);
	putText(image_shuangbian, str_mse, Point(100, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	putText(image_shuangbian, str_psnr, Point(400, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	imshow("双边滤波", image_shuangbian);
}

void duibidu_change1(int pos4,void*)                                                       //对比度调整
{
	Mat image_output1;
	convertScaleAbs(image, image_output1, pos4);
	imshow("对比度调整", image_output1);
}

void duibidu_change2(int pos5, void*)                                                       //对比度调整
{
	Mat image_output1;
	convertScaleAbs(image, image_output1, 1.0, pos5);
	imshow("对比度调整", image_output1);
}

void zhifangtu(Mat image)//仅对灰度图做直方图统计,彩色图通道数不对
{
	Mat image1;
	cvtColor(image, image1, COLOR_BGR2GRAY);//变为灰度图
	int bins = 256;
	int hist_size[] = { bins };
	float range[] = { 0, 256 };
	const float* ranges[] = { range };
	Mat hist;
	int channels[] = { 0 };
	calcHist(
		&image1, 1, channels, Mat(), hist, 1, hist_size, ranges, true, false);
	double max_val;
	minMaxLoc(hist, 0, &max_val, 0, 0);
	int scale = 4;
	int hist_height = 256;
	Mat hist_img = Mat::zeros(hist_height, bins * scale, CV_8UC3);
	for (int i = 0; i < bins; i++)
	{
		float bin_val = hist.at<float>(i);
		int intensity = cvRound(bin_val * hist_height / max_val);
		rectangle(hist_img, Point(i * scale, hist_height - intensity), Point((i + 1) * scale + 1, hist_height), CV_RGB(0, 255, 0), FILLED);
	}
	imshow("image1 Histogram", hist_img);
	
}



int main(int argc, char** argv)
{
	image = imread(argv[1], -1);//用此实现拖动图像进入exe读取
	if (image.empty()) return-1;

	zhifangtu(image);

	char selection;//选择方式
	waitKey(3000);
	printf("1为幂次变换,2为高斯滤波,3为平滑滤波,4为双边滤波,5为对比度调节,请选择\n");
		//scanf_s("%d", &selection);

	while (cin >> selection) {
		if (selection == '1')
		{
			namedWindow("幂次变换的窗口", 0);
			createTrackbar("gamma*100", "幂次变换的窗口", &g_slider_position1, 99, callback_mici);//滑动条的值为伽马值的100倍,伽马取值0-0.99。
			waitKey(0);
			destroyWindow("幂次变换的窗口");
			destroyWindow("幂次变换");
		}

		if (selection == '2')
		{
			namedWindow("高斯滤波的窗口", 0);
			createTrackbar("参数", "高斯滤波的窗口", &g_slider_position2, 20, callback_gaosi);
			waitKey(0);
			destroyWindow("高斯滤波的窗口");
			destroyWindow("高斯滤波的差异值");
			destroyWindow("高斯滤波");
		}

		if (selection == '3')
		{
			namedWindow("平滑滤波的窗口", 0);
			createTrackbar("参数", "平滑滤波的窗口", &g_slider_position3, 20, callback_pinghua);
			waitKey(0);
			destroyWindow("平滑滤波的窗口");
			destroyWindow("平滑滤波的差异值");
			destroyWindow("平滑滤波");
		}
		if (selection == '4')
		{
			namedWindow("双边滤波的窗口", 0);
			createTrackbar("参数", "双边滤波的窗口", &g_slider_position4, 20, callback_shuangbian);
			waitKey(0);
			destroyWindow("双边滤波的窗口");
			destroyWindow("双边滤波的差异值");
			destroyWindow("双边滤波");
		}
		if (selection == '5')
		{
			namedWindow("对比度和亮度变化", 0);
			namedWindow("对比度调整", 0);
			createTrackbar("对比度", "对比度和亮度变化", &g_slider_position4, 20, duibidu_change1);
			createTrackbar("亮度", "对比度和亮度变化", &g_slider_position4, 20, duibidu_change2);
			waitKey(0);
			destroyWindow("对比度调整");
			destroyWindow("对比度和亮度变化");
		}
		if (selection == 'q')  return 0;
	}
	
	return 0;
}

对代码的解释

void MSE_PSNR(Mat image, Mat output)                                      //计算MSE,PSNR
{
	int sum = 0;
	double mse, psnr;
	for (int i = 0; i < image.rows; i++)
	{
		for (int j = 0; j < image.cols; j++)
		{
			sum = sum + (image.at<uchar>(i, j) - output.at<uchar>(i, j)) * (image.at<uchar>(i, j) - output.at<uchar>(i, j));
		}
	}
	mse = sum / (image.rows * image.cols);
	psnr = 10 * log10(255 * 255 / mse);
	//以下为将数值字符串化
	memset(str_mse, 0, sizeof(str_mse));
	memset(str_psnr, 0, sizeof(str_psnr));
	sprintf_s(str_mse, "%.2f", mse);
	sprintf_s(str_psnr, "%.2f", psnr);

}

计算输入输出图像间的MSE,PSNR,相关公式请自行百度。(请注意此前全局变量的定义)
计算得到的int值在mse,psnr中,通过spintf将数值变为字符。(变为字符的说明:因为OpenCV中putText函数只能打印字符)

因为滤波种类太多,我以高斯滤波为例解释下代码。

if (selection == '2')
		{
			namedWindow("高斯滤波的窗口", 0);
			createTrackbar("参数", "高斯滤波的窗口", &g_slider_position2, 20, callback_gaosi);
			waitKey(0);
			destroyWindow("高斯滤波的窗口");
			destroyWindow("高斯滤波的差异值");
			destroyWindow("高斯滤波");
		}

首先是主函数里,定义你输入2时选择高斯滤波,创建高斯滤波的滑动条窗口,主体部分其实都在滑动条的回调函数里。

void callback_gaosi(int pos2, void*)
{
	Mat image_gaosi;
	namedWindow("高斯滤波", 0);
	namedWindow("高斯滤波的差异值", 0);
	GaussianBlur(image, image_gaosi, Size(3, 3), pos2, 0);
	imshow("高斯滤波的差异值", image - image_gaosi);
	MSE_PSNR(image, image_gaosi);
	putText(image_gaosi, str_mse, Point(100, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	putText(image_gaosi, str_psnr, Point(400, 100), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 255, 0), LINE_4, false);
	imshow("高斯滤波", image_gaosi);
}

回调函数标准格式(int pos, void*)。
我把滤波参数设在了调整X,Y的系数值,putText的作用是把MSE,PSNR打印在显示的图片上,字体的选择请随意。

关于直方图统计,有些不足,只实现了对灰度图的直方图统计(当初偷懒,没弄彩色图的三通道直方图统计)。

void zhifangtu(Mat image)//仅对灰度图做直方图统计,彩色图通道数不对
{
	Mat image1;
	cvtColor(image, image1, COLOR_BGR2GRAY);//变为灰度图
	int bins = 256;
	int hist_size[] = { bins };
	float range[] = { 0, 256 };
	const float* ranges[] = { range };
	Mat hist;
	int channels[] = { 0 };
	calcHist(
		&image1, 1, channels, Mat(), hist, 1, hist_size, ranges, true, false);
	double max_val;
	minMaxLoc(hist, 0, &max_val, 0, 0);
	int scale = 4;
	int hist_height = 256;
	Mat hist_img = Mat::zeros(hist_height, bins * scale, CV_8UC3);
	for (int i = 0; i < bins; i++)
	{
		float bin_val = hist.at<float>(i);
		int intensity = cvRound(bin_val * hist_height / max_val);
		rectangle(hist_img, Point(i * scale, hist_height - intensity), Point((i + 1) * scale + 1, hist_height), CV_RGB(0, 255, 0), FILLED);
	}
	imshow("image1 Histogram", hist_img);
	
}

整体代码实现的效果如下

直方图效果
幂次变换
注:按q返回必须在命令窗口外,即q不能输入在命令里,而且需要趁早换成英文输入。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值