(Opencv C++)数字图像处理——图像分割压缩

我们从下面三个方面来进行相应的数字图像处理

一、图象噪声估计

、图象分割

三、图像压缩及解压

 

一、图象噪声估计

常见的噪声模型:    

1、高斯噪声;

2、瑞利噪声;

3、伽马噪声;

4、指数分布噪声;

5、均匀分布噪声;

6、脉冲噪声;

图中噪声分别是:

  • a.高斯噪声分布图;

    b.瑞利噪声分布图;

    c.伽马噪声分布图;

    d.指数噪声分布图;

    e.均匀噪声分布图;

    f.脉冲噪声分布图;

  • 我们将用到的库函数是:

    CV_EXPORTS_W void blur( InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT );(均值滤波函数)
    CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,double sigmaX, double sigmaY = 0,int borderType = BORDER_DEFAULT );(高斯滤波函数)
    CV_EXPORTS_W void medianBlur( InputArray src, OutputArray dst, int ksize );(中值滤波函数)
    CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d,double sigmaColor, double sigmaSpace,int borderType = BORDER_DEFAULT );(双边滤波函数)
    CV_EXPORTS_W int getOptimalDFTSize(int vecsize);(得到最佳IDFT变换尺寸函数)
    CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);(dft变换函数)
    CV_EXPORTS void calcHist( const Mat* images, int nimages,const int* channels, InputArray mask,OutputArray hist, int dims, const int* histSize,const float** ranges, bool uniform = true, bool accumulate = false );(计算直方图函数)
    

    这里我们准备了五种滤波器,分别是均值滤波器,高斯滤波器,中值滤波器,双边滤波器,频域高斯滤波器(主要是滤一些周期噪声)

  • 图像噪声的估计流程如下:

  • 相应的程序如下:

    /*均值滤波函数*/
    Mat Blurefilterfunction(Mat &img)
    {
    	namedWindow("均值滤波【效果图】", CV_WINDOW_AUTOSIZE);
    
    	Mat out;
    	blur(img, out,Size(7, 7)); /*这里选用7*7的内核进行均值滤波*/
    	imshow("均值滤波【效果图】", out);
    
    	return out;
    }
    /*高斯滤波函数*/
    Mat GaussianBlurfilterfunction(Mat &img)
    {
    	namedWindow("高斯滤波【效果图】", CV_WINDOW_AUTOSIZE);
    
    	Mat out;
    	GaussianBlur(img, out, Size(3, 3),0,0); /*这里选用3*3的内核进行高斯滤波*/
    	imshow("高斯滤波【效果图】", out);
    
    	return out;
    }
    /*中值滤波函数*/
    Mat MedianBlurfilterfunction(Mat &img)
    {
    	namedWindow("中值滤波【效果图】", CV_WINDOW_AUTOSIZE);
    
    	Mat out;
    	medianBlur(img, out, 7); 
    	imshow("中值滤波【效果图】", out);
    
    	return out;
    }
    /*双边滤波函数*/
    Mat BilateralBlurfilterfunction(Mat &img)
    {
    	namedWindow("双边滤波【效果图】", CV_WINDOW_AUTOSIZE);
    
    	Mat out;
    	bilateralFilter(img, out, 25, 25 * 2, 25 / 2);
    	imshow("双边滤波【效果图】", out);
    
    	return out;
    }
    /*高斯频域低通滤波器*/
    //**************************************
    //频率域滤波——以高斯低通为例
    //**************************************
    Mat gaussianlbrf(Mat scr, float sigma);//高斯低通滤波器函数
    Mat freqfilt(Mat scr, Mat blur);//频率域滤波函数
    Mat FrequencyDomainGaussFiltering(Mat &input)
    {
    	//Mat input = imread("p3-00-01.tif", CV_LOAD_IMAGE_GRAYSCALE);
    	int w = getOptimalDFTSize(input.cols);  //获取进行dtf的最优尺寸
    	int h = getOptimalDFTSize(input.rows); //获取进行dtf的最优尺寸
    	Mat padded;
    	copyMakeBorder(input, padded, 0, h - input.rows, 0, w - input.cols, BORDER_CONSTANT, Scalar::all(0));  //边界填充
    	padded.convertTo(padded, CV_32FC1); //将图像转换为flaot型
    	Mat gaussianKernel = gaussianlbrf(padded, 30);//高斯低通滤波器
    	Mat out = freqfilt(padded, gaussianKernel);//频率域滤波
    	imshow("结果图 sigma=30", out);
    	return out;
    }
    //*****************高斯低通滤波器***********************
    Mat gaussianlbrf(Mat scr, float sigma)
    {
    	Mat gaussianBlur(scr.size(), CV_32FC1); //,CV_32FC1
    	float d0 = 2 * sigma*sigma;//高斯函数参数,越小,频率高斯滤波器越窄,滤除高频成分越多,图像就越平滑
    	for (int i = 0; i < scr.rows; i++)
    	{
    		for (int j = 0; j < scr.cols; j++)
    		{
    			float d = pow(float(i - scr.rows / 2), 2) + pow(float(j - scr.cols / 2), 2);//分子,计算pow必须为float型
    			gaussianBlur.at<float>(i, j) = expf(-d / d0);//expf为以e为底求幂(必须为float型)
    		}
    	}
    	imshow("高斯低通滤波器", gaussianBlur);
    	return gaussianBlur;
    }
    //*****************频率域滤波*******************
    Mat freqfilt(Mat scr, Mat blur)
    {
    	//***********************DFT*******************
    	Mat plane[] = { scr, Mat::zeros(scr.size() , CV_32FC1) }; //创建通道,存储dft后的实部与虚部(CV_32F,必须为单通道数)
    	Mat complexIm;
    	merge(plane, 2, complexIm);//合并通道 (把两个矩阵合并为一个2通道的Mat类容器)
    	dft(complexIm, complexIm);//进行傅立叶变换,结果保存在自身  
    	//***************中心化********************
    	split(complexIm, plane);//分离通道(数组分离)
    	int cx = plane[0].cols / 2; int cy = plane[0].rows / 2;//以下的操作是移动图像  (零频移到中心)
    	Mat part1_r(plane[0], Rect(0, 0, cx, cy));  //元素坐标表示为(cx,cy)
    	Mat part2_r(plane[0], Rect(cx, 0, cx, cy));
    	Mat part3_r(plane[0], Rect(0, cy, cx, cy));
    	Mat part4_r(plane[0], Rect(cx, cy, cx, cy));
    	Mat temp;
    	part1_r.copyTo(temp);  //左上与右下交换位置(实部)
    	part4_r.copyTo(part1_r);
    	temp.copyTo(part4_r);
    	part2_r.copyTo(temp);  //右上与左下交换位置(实部)
    	part3_r.copyTo(part2_r);
    	temp.copyTo(part3_r);
    	Mat part1_i(plane[1], Rect(0, 0, cx, cy));  //元素坐标(cx,cy)
    	Mat part2_i(plane[1], Rect(cx, 0, cx, cy));
    	Mat part3_i(plane[1], Rect(0, cy, cx, cy));
    	Mat part4_i(plane[1], Rect(cx, cy, cx, cy));
    	part1_i.copyTo(temp);  //左上与右下交换位置(虚部)
    	part4_i.copyTo(part1_i);
    	temp.copyTo(part4_i);
    	part2_i.copyTo(temp);  //右上与左下交换位置(虚部)
    	part3_i.copyTo(part2_i);
    	temp.copyTo(part3_i);
    	//*****************滤波器函数与DFT结果的乘积****************
    	Mat blur_r, blur_i, BLUR;
    	multiply(plane[0], blur, blur_r); //滤波(实部与滤波器模板对应元素相乘)
    	multiply(plane[1], blur, blur_i);//滤波(虚部与滤波器模板对应元素相乘)
    	Mat plane1[] = { blur_r, blur_i };
    	merge(plane1, 2, BLUR);//实部与虚部合并
    	  //*********************得到原图频谱图***********************************
    	magnitude(plane[0], plane[1], plane[0]);//获取幅度图像,0通道为实部通道,1为虚部,因为二维傅立叶变换结果是复数  
    	plane[0] += Scalar::all(1);  //傅立叶变o换后的图片不好分析,进行对数处理,结果比较好看 
    	log(plane[0], plane[0]);    // float型的灰度空间为[0,1])
    	normalize(plane[0], plane[0], 1, 0, CV_MINMAX);  //归一化便于显示
    	imshow("原图像频谱图", plane[0]);
    	idft(BLUR, BLUR);	//idft结果也为复数
    	split(BLUR, plane);//分离通道,主要获取通道
    	magnitude(plane[0], plane[1], plane[0]);  //求幅值(模)
    	normalize(plane[0], plane[0], 1, 0, CV_MINMAX);  //归一化便于显示
    	return plane[0];//返回参数
    }
    
    /*绘制直方图函数*/
    void Drawing_one_dimensionalhistogram(Mat &img)
    {
    	/*输入图像转化为灰度图像*/
    	//cvtColor(img, img, CV_BGR2GRAY);
    	/*定义变量*/
    	MatND dstHist;
    	int dims = 1;
    	float hranges[] = { 0,255 };
    	const float *ranges[] = { hranges };   /*这里需要为const类型*/
    	int size = 256;
    	int channels = 0;
    
    	/*计算图像的直方图*/
    	calcHist(&img, 1, &channels, Mat(), dstHist, dims, &size, ranges);
    	int scale = 1;
    
    	Mat dstImage(size * scale, size, CV_8U, Scalar(0));
    	/*获取最大值和最小值*/
    	double minValue = 0;
    	double maxValue = 0;
    	minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
    	/*绘制出直方图*/
    	int hpt = saturate_cast<int>(0.9 * size);
    	for (int i = 0; i < 256; i++)
    	{
    		float binValue = dstHist.at<float>(i);
    		int realValue = saturate_cast<int>(binValue * hpt / maxValue);
    		rectangle(dstImage, Point(i*scale, size - 1), Point((i + 1)*scale - 1, size - realValue), Scalar(255));
    	}
    	imshow("图像的一维直方图", dstImage);
    }

    例如下列图片的直方图:

  • 从上到下分别为瑞利噪声(周期噪声),高斯噪声,均匀噪声(周期噪声)

  • 首先我们先对瑞利噪声在空域滤波,效果图如下:

  • 频域滤波,效果图如下:

  • 对于高斯噪声,我们只需要对其高斯滤波即可,但是这里需要进行相应的参数调节,效果图如下:

  • 最后一个均匀噪声,也属于周期噪声的一种,这里我们需要对其在频域滤波,效果图如下:

  • 、图象分割

  • 大津分割流程图:

  • 我们使用到的库函数为:

    CV_EXPORTS_W double threshold( InputArray src, OutputArray dst,double thresh, double maxval, int type );(阀值化函数)
    

    相应的程序如下:

    /*计算大津分割的阈值*/
    int OtsuAlgThreshold(const Mat image)
    {
    	if (image.channels() != 1)
    	{
    		cout << "Please input Gray-image!" << endl;
    		return 0;
    	}
    	int T = 0; //Otsu算法阈值
    	double varValue = 0; //类间方差中间值保存
    	double w0 = 0; //前景像素点数所占比例
    	double w1 = 0; //背景像素点数所占比例
    	double u0 = 0; //前景平均灰度
    	double u1 = 0; //背景平均灰度
    	double Histogram[256] = { 0 }; //灰度直方图,下标是灰度值,保存内容是灰度值对应的像素点总数
    	int Histogram1[256] = { 0 };
    	uchar *data = image.data;
    	double totalNum = image.rows*image.cols; //像素总数
    	//计算灰度直方图分布,Histogram数组下标是灰度值,保存内容是灰度值对应像素点数
    	for (int i = 0; i < image.rows; i++)   //为表述清晰,并没有把rows和cols单独提出来
    	{
    		for (int j = 0; j < image.cols; j++)
    		{
    			Histogram[data[i*image.step + j]]++;
    			Histogram1[data[i*image.step + j]]++;
    		}
    
    	}
    	//***********画出图像直方图********************************
    	Mat image1(255, 255, CV_8UC3);
    	for (int i = 0; i < 255; i++)
    	{
    		Histogram1[i] = Histogram1[i] % 200;
    		line(image1, Point(i, 235), Point(i, 235 - Histogram1[i]), Scalar(255, 0, 0), 1, 8, 0);
    		if (i % 50 == 0)
    		{
    			char ch[255];
    			sprintf_s(ch, "%d", i);
    			string str = ch;
    			putText(image1, str, Point(i, 250), 1, 1, Scalar(0, 0, 255));
    		}
    	}
    
    	//***********画出图像直方图********************************
    	for (int i = 0; i < 255; i++)
    	{
    		//每次遍历之前初始化各变量
    		w1 = 0;	
    		u1 = 0;	
    		w0 = 0;
    		u0 = 0;
    		//***********背景各分量值计算**************************
    		for (int j = 0; j <= i; j++) //背景部分各值计算
    		{
    			w1 += Histogram[j];  //背景部分像素点总数
    			u1 += j * Histogram[j]; //背景部分像素总灰度和
    		}
    		if (w1 == 0) //背景部分像素点数为0时退出
    		{
    			break;
    		}
    		u1 = u1 / w1; //背景像素平均灰度
    		w1 = w1 / totalNum; // 背景部分像素点数所占比例
    		//***********背景各分量值计算**************************
    		//***********前景各分量值计算**************************
    		for (int k = i + 1; k < 255; k++)
    		{
    			w0 += Histogram[k];  //前景部分像素点总数
    			u0 += k * Histogram[k]; //前景部分像素总灰度和
    		}
    		if (w0 == 0) //前景部分像素点数为0时退出
    		{
    			break;
    		}
    		u0 = u0 / w0; //前景像素平均灰度
    		w0 = w0 / totalNum; // 前景部分像素点数所占比例
    		//***********前景各分量值计算**************************
    		//***********类间方差计算******************************
    		double varValueI = w0 * w1*(u1 - u0)*(u1 - u0); //当前类间方差计算
    		if (varValue < varValueI)
    		{
    			varValue = varValueI;
    			T = i;
    		}
    	}
    	//画出以T为阈值的分割线
    	line(image1, Point(T, 235), Point(T, 0), Scalar(0, 0, 255), 2, 8);
    	imshow("直方图", image1);
    	return T;
    }
    
    //***************Otsu算法通过求类间方差极大值求自适应阈值*****************
    Mat Otsusegmentation(Mat &img)
    {
    	cvtColor(img, img, CV_RGB2GRAY);
    	Mat imageOutput;
    	Mat imageOtsu;
    	int thresholdValue = OtsuAlgThreshold(img);
    	cout << "类间方差为: " << thresholdValue << endl;
    	threshold(img, imageOutput, thresholdValue, 255, CV_THRESH_BINARY);
    	threshold(img, imageOtsu, 0, 255, CV_THRESH_OTSU); //Opencv Otsu算法;
    	imshow("大津分割【效果图】", imageOutput);
    	imshow("Opencv Otsu【效果图】", imageOtsu);
    	return imageOutput;
    }

    首先我们先计算一张图片的阈值,得到其最小类方差:

  • 然后进行相应的阈值值化操作:

  • 图像分割还有一种称为交互式分割,这里我们直接使用库函数:

    CV_EXPORTS_W void grabCut( InputArray img, InputOutputArray mask, Rect rect, InputOutputArray bgdModel, InputOutputArray fgdModel,  int iterCount, int mode = GC_EVAL );
    img:待分割的源图像,必须是8位3通道(CV_8UC3)图像,在处理的过程中不会被修改;    
    mask:掩码图像,大小和原图像一致。可以有如下几种取值: GC_BGD(=0),背景; GC_FGD(=1),前景; GC_PR_BGD(=2),可能的背景; GC_PR_FGD(=3),可能的前景。 
    rect:用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;
     bgdModel:背景模型,如果为null,函数内部会自动创建一个bgdModel;
     fgdModel:前景模型,如果为null,函数内部会自动创建一个fgdModel;
     iterCount:迭代次数,必须大于0; 
    mode:用于指示grabCut函数进行什么操作。可以有如下几种选择: GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut; GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut; GC_EVAL(=2),执行分割。
    

    代码如下:

    /*grath_cut*/
    void grathcutfunctiong(Mat &img)
    {
    	Mat bgModel, fgModel, mask;
    	Rect rect;
    	rect.x = 50;
    	rect.y = 45;
    	rect.width = img.cols - (rect.x << 1);
    	rect.height = img.rows - (rect.y << 1);
    	rectangle(img, rect, Scalar(0, 0, 255), 3, 8, 0);//用矩形画矩形窗  
    
    	//循环执行3次,这个可以自己设置
    	grabCut(img, mask, rect, bgModel, fgModel, 3, GC_INIT_WITH_RECT);
    	compare(mask, GC_PR_FGD, mask, CMP_EQ);
    	Mat foreground(img.size(), CV_8UC3, Scalar(255, 255, 255));
    	img.copyTo(foreground, mask);
    	imshow("foreground", foreground);
    }

    相应的效果图如下:

  • 这里再附一个分水岭算法的代码,有兴趣的可以试一试:

    Mat g_maskImage, g_scrImage;
    Point prevPt(-1, -1);
    static void ShowHelpText();
    static void on_Mouse(int event, int x, int y, int flags, void*);
    /*分水岭算法*/
    void Watershedfunction(Mat &g_scrImage)
    {
    	/*初始化掩膜和灰度图*/
    	Mat scrImage, grayImage;
    	grayImage.copyTo(scrImage);
    	cvtColor(g_scrImage, g_maskImage, COLOR_BGR2GRAY);
    	cvtColor(g_maskImage, grayImage, COLOR_GRAY2BGR);
    	g_maskImage = Scalar::all(0);
    	/*设置鼠标回调函数*/
    	setMouseCallback(WINDOW_NAME, on_Mouse, 0);
    	/*轮询按键,进行处理*/
    	while (1)
    	{
    		/*获取键值*/
    		int c = waitKey(0);
    		/*若按键值为ESC时,退出*/
    		if ((char)c == 27)
    		{
    			break;
    		}
    		/*若按键值为2时,恢复原图*/
    		if ((char)c == '2')
    		{
    			g_maskImage = Scalar::all(0);
    			scrImage.copyTo(g_scrImage);
    			imshow("image",g_scrImage);
    		}
    		/*若按键值为1或者空格,则进行处理*/
    		if ((char)c == '1' || (char)c == ' ')
    		{
    			/*定义一些参数*/
    			int i, j, compCount = 0;
    			vector<vector<Point>> contours;
    			vector<Vec4i> hierarchy;
    
    			/*寻找轮廓*/
    			findContours(g_maskImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    			/*轮廓为空时的处理*/
    			if (contours.empty())
    				continue;
    			/*复制掩膜*/
    			Mat maskImage(g_maskImage.size(), CV_32S);
    			maskImage = Scalar::all(0);
    			/*循环绘制出轮廓*/
    			for (int index = 0; index > 0; index = hierarchy[index][0], compCount++)
    				drawContours(maskImage, contours, index, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_FAST16_MAX);
    			/*compCount为0时的处理*/
    			if (compCount == 0)
    				continue;
    			/*生成随机颜色*/
    			vector<Vec3b> colorTab;
    			for (i = 0; i < compCount; i++)
    			{
    				int b = theRNG().uniform(0, 255);
    				int g = theRNG().uniform(0, 255);
    				int r = theRNG().uniform(0, 255);
    
    				colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    			}
    			/*计算处理时间并输出到窗口中*/
    			double dTime = (double)getTickCount();
    			watershed(scrImage, maskImage);
    			dTime = (double)getTickCount() - dTime;
    			printf("\t处理时间 = %gms\n",dTime*1000./getTickFrequency());
    
    			/*双层循环,将分水岭图像遍历存入watershedImage中*/
    			Mat watershedImage(maskImage.size(), CV_8UC3);
    			for(i=0;i<maskImage.rows;i++)
    				for (j = 0; j < maskImage.cols; j++)
    				{
    					int index = maskImage.at<int>(i, j);
    					if (index == -1)
    						watershedImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
    					else if (index <= 0 || index > compCount)
    						watershedImage.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
    					else
    						watershedImage.at<Vec3b>(i, j) = colorTab[index - 1];
    				}
    			watershedImage = watershedImage * 0.5 + grayImage * 0.5;
    			imshow("watershed transform",watershedImage);
    		}
    	}
    }
    static void on_Mouse(int event, int x, int y, int flags, void *)
    {
    	/*处理鼠标不在窗口中的情况*/
    	if (x < 0 || x >= g_scrImage.cols || y < 0 || y >= g_maskImage.rows)
    		return;
    	/*数理鼠标左键相关信息*/
    	if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
    		prevPt = Point(-1, -1);
    	else if (event == EVENT_LBUTTONDOWN)
    		prevPt = Point(x, y);
    	/*鼠标左键按下并移动,绘制出白色线条*/
    	else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
    	{
    		Point pt(x, y);
    		if (prevPt.x < 0)
    			prevPt = pt;
    		line(g_maskImage, prevPt, pt, Scalar::all(255), 5, 8, 0);
    		line(g_scrImage, prevPt, pt, Scalar::all(255), 5, 8, 0);
    		prevPt = pt;
    		imshow(WINDOW_NAME,g_scrImage);
    	}
    }
    

    三、图像压缩及解压

  • (1)、霍夫曼编码压缩图片:

  • 其流程图如下:

  • 代码如下:

  • /*哈夫曼编码*/
    //几个全局变量,存放读入图像的位图数据、宽、高、颜色表及每像素所占位数(比特) 
    //此处定义全局变量主要为了后面的图像数据访问及图像存储作准备
    unsigned char *pBmpBuf;//读入图像数据的指针
    int bmpWidth;//图像的宽
    int bmpHeight;//图像的高
    int imgSpace;//图像所需空间
    RGBQUAD *pColorTable;//颜色表指针
    int biBitCount;//图像类型
    char str[100];//文件名称 
    int Num[300];//各灰度值出现的次数 
    float Feq[300];//各灰度值出现的频率 
    unsigned char *lpBuf;//指向图像像素的指针
    unsigned char *m_pDib;//存放打开文件的DIB
    int NodeNum;	//Huffman树总节点个数
    int NodeStart;	//Huffman树起始节点
    struct Node 
    {		//Huffman树节点
    	int color;		//记录叶子节点的灰度值(非叶子节点为 -1)
    	int lson, rson;	//节点的左右儿子(若没有则为 -1)
    	int num;		//节点的数值(编码依据)
    	int mark;		//记录节点是否被用过(用过为1,没用过为0)
    }node[600];
    char CodeStr[300][300];	//记录编码值
    int CodeLen[300];		//编码长度
    bool ImgInf[8000000];	//图像信息
    int InfLen;				//图像信息长度
    /***********************************************************************
    * 函数名称:
    * readBmp()
    *
    *函数参数:
    *  char *bmpName -文件名字及路径
    *
    *返回值:
    *   0为失败,1为成功
    *
    *说明:给定一个图像文件名及其路径,读图像的位图数据、宽、高、颜色表及每像素
    *      位数等数据进内存,存放在相应的全局变量中
    ***********************************************************************/
    bool readBmp(char *bmpName)
    {
    	//二进制读方式打开指定的图像文件
    	FILE *fp = fopen(bmpName, "rb");
    	if (fp == 0)
    	{
    		printf("未找到指定文件!\n");
    		return 0;
    	}
    	//跳过位图文件头结构BITMAPFILEHEADER
    	fseek(fp, sizeof(BITMAPFILEHEADER), 0);
    	//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
    	BITMAPINFOHEADER head;
    	fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);
    	//获取图像宽、高、每像素所占位数等信息
    	bmpWidth = head.biWidth;
    	bmpHeight = head.biHeight;
    	biBitCount = head.biBitCount;
    	//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
    	int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
    	//灰度图像有颜色表,且颜色表表项为256
    	if (biBitCount == 8)
    	{
    		//申请颜色表所需要的空间,读颜色表进内存
    		pColorTable = new RGBQUAD[256];
    		fread(pColorTable, sizeof(RGBQUAD), 256, fp);
    	}
    	//申请位图数据所需要的空间,读位图数据进内存
    	pBmpBuf = new unsigned char[lineByte * bmpHeight];
    	fread(pBmpBuf, 1, lineByte * bmpHeight, fp);
    	//关闭文件
    	fclose(fp);
    	return 1;
    }
    /***********************************************************************
    保存信息
    ***********************************************************************/
    //二进制转十进制
    int Change2to10(int pos) 
    {
    	int i, j, two = 1;
    	j = 0;
    	for (i = pos + 7; i >= pos; i--) 
    	{
    		j += two * ImgInf[i];
    		two *= 2;
    	}
    	return j;
    }
    //保存Huffman编码树
    int saveInfo(char *writePath, int lineByte) 
    {
    	int i, j ;
    	FILE *fout;
    	fout = fopen(writePath, "w");
    	fprintf(fout, "%d %d %d\n", NodeStart, NodeNum, InfLen);//输出起始节点、节点总数、图像所占空间
    	for (i = 0; i < NodeNum; i++) 
    	{		//输出Huffman树
    		fprintf(fout, "%d %d %d\n", node[i].color, node[i].lson, node[i].rson);
    	}
    	fclose(fout);
    	return 0;
    }
    //保存文件
    bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height,
    	int biBitCount, RGBQUAD *pColorTable)
    {
    
    	//如果位图数据指针为0,则没有数据传入,函数返回
    	if (!imgBuf)
    		return 0;
    	//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
    	int colorTablesize = 0;
    	if (biBitCount == 8)
    		colorTablesize = 1024;
    	//待存储图像数据每行字节数为4的倍数
    	int lineByte = (width * biBitCount / 8 + 3) / 4 * 4;
    	//以二进制写的方式打开文件
    	FILE *fp = fopen(bmpName, "wb");
    	if (fp == 0) return 0;
    	//申请位图文件头结构变量,填写文件头信息
    	BITMAPFILEHEADER fileHead;
    	fileHead.bfType = 0x4D42;//bmp类型
    	//bfSize是图像文件4个组成部分之和
    	fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte * height;
    	fileHead.bfReserved1 = 0;
    	fileHead.bfReserved2 = 0;
    	//bfOffBits是图像文件前三个部分所需空间之和
    	fileHead.bfOffBits = 54 + colorTablesize;
    	//写文件头进文件
    	fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
    	//申请位图信息头结构变量,填写信息头信息
    	BITMAPINFOHEADER head;
    	head.biBitCount = biBitCount;
    	head.biClrImportant = 0;
    	head.biClrUsed = 0;
    	head.biCompression = 0;
    	head.biHeight = height;
    	head.biPlanes = 1;
    	head.biSize = 40;
    	head.biSizeImage = lineByte * height;
    	head.biWidth = width;
    	head.biXPelsPerMeter = 0;
    	head.biYPelsPerMeter = 0;
    	//写位图信息头进内存
    	fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);
    	//如果灰度图像,有颜色表,写入文件 
    	if (biBitCount == 8)
    		fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
    	//写位图数据进文件
    	fwrite(imgBuf, InfLen / 8, 1, fp);
    	//关闭文件
    	fclose(fp);
    	return 1;
    }
    /*********************
    Huffman编码图像解码
    *********************/
    //读入编码图像
    bool readHuffman(char *Name)
    {
    	int i;
    	char NameStr[100];
    	//读取Huffman编码信息和编码树
    	strcpy(NameStr, Name);
    	strcat(NameStr, ".bpt");
    	FILE *fin = fopen(NameStr, "r");
    	if (fin == 0) 
    	{
    		printf("未找到指定文件!\n");
    		return 0;
    	}
    	fscanf(fin, "%d %d %d", &NodeStart, &NodeNum, &InfLen);
    	//printf("%d %d %d\n",NodeStart,NodeNum,InfLen);
    	for (i = 0; i < NodeNum; i++)
    	{
    		fscanf(fin, "%d %d %d", &node[i].color, &node[i].lson, &node[i].rson);
    	}
    	//二进制读方式打开指定的图像文件
    	strcpy(NameStr, Name);
    	strcat(NameStr, ".bhd");
    	FILE *fp = fopen(NameStr, "rb");
    	if (fp == 0)
    	{
    		printf("未找到指定文件!\n");
    		return 0;
    	}
    	//跳过位图文件头结构BITMAPFILEHEADER
    	fseek(fp, sizeof(BITMAPFILEHEADER), 0);
    	//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
    	BITMAPINFOHEADER head;
    	fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);
    	//获取图像宽、高、每像素所占位数等信息
    	bmpWidth = head.biWidth;
    	bmpHeight = head.biHeight;
    	biBitCount = head.biBitCount;
    	//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
    	int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
    	//灰度图像有颜色表,且颜色表表项为256
    	if (biBitCount == 8) {
    		//申请颜色表所需要的空间,读颜色表进内存
    		pColorTable = new RGBQUAD[256];
    		fread(pColorTable, sizeof(RGBQUAD), 256, fp);
    	}
    	//申请位图数据所需要的空间,读位图数据进内存
    	pBmpBuf = new unsigned char[lineByte * bmpHeight];
    	fread(pBmpBuf, 1, InfLen / 8, fp);
    	//关闭文件
    	fclose(fp);
    	return 1;
    }
    void HuffmanDecode()
    {
    	//获取编码信息
    	int i, j, tmp;
    	int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
    	for (i = 0; i < InfLen / 8; i++)
    	{
    		j = i * 8 + 7;
    		tmp = *(pBmpBuf + i);
    		while (tmp > 0)
    		{
    			ImgInf[j] = tmp % 2;
    			tmp /= 2;
    			j--;
    		}
    	}
    	//解码
    	int p = NodeStart;	//遍历指针位置
    	j = 0;
    	i = 0;
    	do
    	{
    		if (node[p].color >= 0)
    		{
    			*(pBmpBuf + j) = node[p].color;
    			j++;
    			p = NodeStart;
    		}
    		if (ImgInf[i] == 1)
    			p = node[p].lson;
    		else if (ImgInf[i] == 0)
    			p = node[p].rson;
    		i++;
    	} while (i <= InfLen);
    }
    /*********************
    Huffman编码
    *********************/
    //Huffman编码初始化
    void HuffmanCodeInit()
    {
    	int i;
    	for (i = 0; i < 256; i++)//灰度值记录清零
    		Num[i] = 0;
    	//初始化哈夫曼树
    	for (i = 0; i < 600; i++)
    	{
    		node[i].color = -1;
    		node[i].lson = node[i].rson = -1;
    		node[i].num = -1;
    		node[i].mark = 0;
    	}
    	NodeNum = 0;
    }
    //深搜遍历Huffman树获取编码值
    char CodeTmp[300];
    void dfs(int pos, int len)
    {
    	//遍历左儿子
    	if (node[pos].lson != -1)
    	{
    		CodeTmp[len] = '1';
    		dfs(node[pos].lson, len + 1);
    	}
    	else
    	{
    		if (node[pos].color != -1)
    		{
    			CodeLen[node[pos].color] = len;
    			CodeTmp[len] = '\0';
    			strcpy(CodeStr[node[pos].color], CodeTmp);
    		}
    	}
    	//遍历右儿子
    	if (node[pos].lson != -1)
    	{
    		CodeTmp[len] = '0';
    		dfs(node[pos].rson, len + 1);
    	}
    	else {
    		if (node[pos].color != -1)
    		{
    			CodeLen[node[pos].color] = len;
    			CodeTmp[len] = '\0';
    			strcpy(CodeStr[node[pos].color], CodeTmp);
    		}
    	}
    }
    //寻找值最小的节点
    int MinNode()
    {
    	int i, j = -1;
    	for (i = 0; i < NodeNum; i++)
    		if (!node[i].mark)
    			if (j == -1 || node[i].num < node[j].num)
    				j = i;
    	if (j != -1)
    	{
    		NodeStart = j;
    		node[j].mark = 1;
    	}
    	return j;
    }
    //编码主函数
    void HuffmanCode()
    {
    	int i, j, k, a, b;
    	for (i = 0; i < 256; i++)
    	{//创建初始节点
    		Feq[i] = (float)Num[i] / (float)(bmpHeight * bmpWidth);//计算灰度值频率
    		if (Num[i] > 0)
    		{
    			node[NodeNum].color = i;
    			node[NodeNum].num = Num[i];
    			node[NodeNum].lson = node[NodeNum].rson = -1;	//叶子节点无左右儿子
    			NodeNum++;
    		}
    	}
    	while (1)
    	{	//找到两个值最小的节点,合并成为新的节点
    		a = MinNode();
    		if (a == -1)
    			break;
    		b = MinNode();
    		if (b == -1)
    			break;
    		//构建新节点
    		node[NodeNum].color = -1;
    		node[NodeNum].num = node[a].num + node[b].num;
    		node[NodeNum].lson = a;
    		node[NodeNum].rson = b;
    		NodeNum++;
    	}
    	//根据建好的Huffman树编码(深搜实现)
    	dfs(NodeStart, 0);
    	//屏幕输出编码
    	int sum = 0;
    	printf("Huffman编码信息如下:\n");
    	for (i = 0; i < 256; i++)
    		if (Num[i] > 0)
    		{
    			sum += CodeLen[i] * Num[i];
    			printf("灰度值:%3d  频率: %f  码长: %2d  编码: %s\n",i,Feq[i],CodeLen[i],CodeStr[i]);
    		}
    			printf("原始总码长:%d\n",bmpWidth * bmpHeight * 8);
    	 		printf("Huffman编码总码长:%d\n",sum);
    			printf("压缩比:%.3f : 1\n",(float)(bmpWidth * bmpHeight * 8) / (float)sum);
    	for (i = 0; i < 256; i++)
    		if (Num[i] > 0)
    		{
    			sum += CodeLen[i] * Num[i];
    		}
    			//记录图像信息
    	InfLen = 0;
    	int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
    	for (i = 0; i < bmpHeight; i++)
    		for (j = 0; j < bmpWidth; j++)
    		{
    			lpBuf = (unsigned char *)pBmpBuf + lineByte * i + j;
    			for (k = 0; k < CodeLen[*(lpBuf)]; k++)
    			{
    				ImgInf[InfLen++] = (int)(CodeStr[*(lpBuf)][k] - '0');
    			}
    		}
    	//再编码数据
    	j = 0;
    	for (i = 0; i < InfLen;)
    	{
    		*(pBmpBuf + j) = Change2to10(i);
    		i += 8;
    		j++;
    	}
    }
    /******************************
    主函数
    ******************************/
    void Huffmanfunction()
    {
    	int ord;//命令 
    	char c;
    	int i, j;
    	clock_t start, finish;
    	int total_time;
    	//	CString str;
    	while (1)
    	{
    		printf("本程序提供以下功能\n\n\t1.256色灰度BMP图像Huffman编码\n\t2.Huffman编码BMP文件解码\n\t3.退出\n\n请选择需要执行的命令:");
    		scanf("%d%c", &ord, &c);
    		if (ord == 1)
    		{
    			printf("\n---256色灰度BMP图像Huffman编码---\n");
    			printf("\n请输入要编码图像名称:");
    			scanf("%s", str);
    			//读入指定BMP文件进内存
    			char readPath[100];
    			strcpy(readPath, str);
    			strcat(readPath, ".bmp");
    			if (readBmp(readPath))
    			{
    				int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
    				if (biBitCount == 8)
    				{
    					//编码初始化
    					HuffmanCodeInit();
    					//计算每个灰度值出现的次数 
    					for (i = 0; i < bmpHeight; i++)
    						for (j = 0; j < bmpWidth; j++)
    						{
    							lpBuf = (unsigned char *)pBmpBuf + lineByte * i + j;
    							Num[*(lpBuf)] += 1;
    						}
    					//调用编码
    					start = clock();
    					HuffmanCode();
    					finish = clock();
    					total_time = (finish - start);
    					printf("识别一张耗时: %d毫秒", total_time);
    					//将图像数据存盘
    					char writePath[100];
    					//保存编码后的bmp
    					strcpy(writePath, str);
    					strcat(writePath, "_Huffman.bhd");
    					saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);
    					//保存Huffman编码信息和编码树
    					strcpy(writePath, str);
    					strcat(writePath, "_Huffman.bpt");
    					saveInfo(writePath, lineByte);
    					printf("\n编码完成!编码信息保存在 %s_Huffman 文件中\n\n", str);
    				}
    				else
    				{
    					printf("本程序只支持256色BMP编码!\n");
    				}
    				//清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间
    				delete[]pBmpBuf;
    				if (biBitCount == 8)
    					delete[]pColorTable;
    			}
    			printf("\n-----------------------------------------------\n\n\n");
    		}
    		else if (ord == 2)
    		{
    			printf("\n---Huffman编码BMP文件解码---\n");
    			printf("\n请输入要解码文件名称:");
    			scanf("%s", str);
    			//编码解码初始化
    			HuffmanCodeInit();
    			if (readHuffman(str))
    			{	//读取文件
    				HuffmanDecode();	//Huffman解码
    				//将图像数据存盘
    				char writePath[100];
    				//保存解码后的bmp
    				strcpy(writePath, str);
    				strcat(writePath, "_Decode.bmp");
    				InfLen = bmpWidth * bmpHeight * 8;
    				saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);
    				system(writePath);
    				printf("\n解码完成!保存为 %s_Decode.bmp\n\n", str);
    			}
    			printf("\n-----------------------------------------------\n\n\n");
    		}
    		else if (ord == 3)
    			break;
    	}
    }
  • 效果图如下:

  •  

    (2)、使用DCT变换进行图像压缩

  • 其流程图如下:

  • 代码如下:

  • /*DCT压缩*/
    void blkproc_DCT(Mat);	//功能等同于Matlab的blkproc函数(blkproc(I,[8 8],'P1*x*P2',g,g')),用于对图像做8*8分块DCT
    Mat blkproc_IDCT(Mat);	//功能等同于Matlab的blkproc函数(blkproc(I,[8 8],'P1*x*P2',g',g)),用于做8*8分块DCT逆变换,恢复原始图像
    void regionalCoding(Mat);	//区域编码函数,功能等同于Matlab的blkproc函数(blkproc(I1,[8 8],'P1.*x',a))
    void thresholdCoding(Mat);	//阈值编码函数,功能等同于Matlab的blkproc函数(blkproc(I1,[8 8],'P1.*x',a))
    double get_medianNum(Mat &);	//获取矩阵的中值,用于阈值编码
    
    void ImageCompression_DCT(Mat &ucharImg)
    {
    	//Mat ucharImg = imread("3.jpg", 0);	//以灰度图的形式读入原始的图像
    	//imshow("srcImg", ucharImg);
    	Mat doubleImg,OrignalImg;
    	OrignalImg = ucharImg.clone();
    	ucharImg.convertTo(doubleImg, CV_64F);	//将原始图像转换成double类型的图像,方便后面的8*8分块DCT变换
    	blkproc_DCT(doubleImg);	//对原图片做8*8分块DCT变换
    	//分别进行区域编码和阈值编码
    	Mat doubleImgRegion, doubleImgThreshold;
    	doubleImgRegion = doubleImg.clone();
    	doubleImgThreshold = doubleImg.clone();
    	regionalCoding(doubleImgRegion);	//对DCT变换后的系数进行区域编码
    	thresholdCoding(doubleImgThreshold);	//对DCT变换后的系数进行阈值编码
    	//进行逆DCT变换
    	Mat ucharImgRegion, ucharImgThreshold, differenceimage;
    	ucharImgRegion = blkproc_IDCT(doubleImgRegion);
    	//namedWindow("RegionalCoding", CV_WINDOW_AUTOSIZE);
    	imshow("RegionalCoding", ucharImgRegion);
    	ucharImgThreshold = blkproc_IDCT(doubleImgThreshold);
    	//namedWindow("ThresholdCoding", CV_WINDOW_AUTOSIZE);
    	imshow("ThresholdCoding", ucharImgThreshold);
    	absdiff(ucharImgRegion, OrignalImg, differenceimage);
    	imshow("两幅图像的差值",differenceimage);
    
    }
    
    
    
    void blkproc_DCT(Mat doubleImgTmp)
    {
    	Mat ucharImgTmp;
    	Mat DCTMat = Mat(8, 8, CV_64FC1);	//用于DCT变换的8*8的矩阵
    	Mat DCTMatT;	//DCTMat矩阵的转置
    	Mat ROIMat = Mat(8, 8, CV_64FC1);	//用于分块处理的时候在原图像上面移动
    	double a = 0, q;	//DCT变换的系数
    	for (int i = 0; i < DCTMat.rows; i++)
    	{
    		for (int j = 0; j < DCTMat.cols; j++)
    		{
    			if (i == 0)
    			{
    				a = pow(1.0 / DCTMat.rows, 0.5);
    			}
    			else
    			{
    				a = pow(2.0 / DCTMat.rows, 0.5);
    			}
    			q = ((2 * j + 1)*i*M_PI) / (2 * DCTMat.rows);
    			DCTMat.at<double>(i, j) = a * cos(q);
    		}
    	}
    	DCTMatT = DCTMat.t();
    	//ROIMat在doubleImgTmp以8为步长移动,达到与Matlab中的分块处理函数blkproc相同的效果
    	//此程序中,若图片的高或者宽不是8的整数倍的话,最后的不足8的部分不进行处理
    	int rNum = doubleImgTmp.rows / 8;
    	int cNum = doubleImgTmp.cols / 8;
    	for (int i = 0; i < rNum; i++)
    	{
    		for (int j = 0; j < cNum; j++)
    		{
    			ROIMat = doubleImgTmp(Rect(j * 8, i * 8, 8, 8));
    			ROIMat = DCTMat * ROIMat*DCTMatT;
    		}
    	}
    	doubleImgTmp.convertTo(ucharImgTmp, CV_8U);
    	imshow("DCTImg", ucharImgTmp);
    }
    Mat blkproc_IDCT(Mat doubleImgTmp)
    
    {
    	//与blkproc_DCT几乎一样,唯一的差别在于:ROIMat = DCTMatT*ROIMat*DCTMat(转置矩阵DCTMatT和DCTMat交换了位置)
    	Mat ucharImgTmp;
    	Mat DCTMat = Mat(8, 8, CV_64FC1);
    	Mat DCTMatT;
    	Mat ROIMat = Mat(8, 8, CV_64FC1);
    	double a = 0, q;
    	for (int i = 0; i < DCTMat.rows; i++)
    	{
    		for (int j = 0; j < DCTMat.cols; j++)
    		{
    			if (i == 0)
    			{
    				a = pow(1.0 / DCTMat.rows, 0.5);
    			}
    			else
    			{
    				a = pow(2.0 / DCTMat.rows, 0.5);
    			}
    			q = ((2 * j + 1)*i*M_PI) / (2 * DCTMat.rows);
    			DCTMat.at<double>(i, j) = a * cos(q);
    		}
    	}
    	DCTMatT = DCTMat.t();
    	int rNum = doubleImgTmp.rows / 8;
    	int cNum = doubleImgTmp.cols / 8;
    	for (int i = 0; i < rNum; i++)
    	{
    		for (int j = 0; j < cNum; j++)
    		{
    			ROIMat = doubleImgTmp(Rect(j * 8, i * 8, 8, 8));
    			ROIMat = DCTMatT * ROIMat*DCTMat;
    		}
    	}
    	doubleImgTmp.convertTo(ucharImgTmp, CV_8U);
    	return ucharImgTmp;
    }
    void regionalCoding(Mat doubleImgTmp)
    {
    	int rNum = doubleImgTmp.rows / 8;
    	int cNum = doubleImgTmp.cols / 8;
    	Mat ucharImgTmp;
    	Mat ROIMat = Mat(8, 8, CV_64FC1);	//用于分块处理的时候在原图像上面移动
    	for (int i = 0; i < rNum; i++)
    	{
    		for (int j = 0; j < cNum; j++)
    		{
    			ROIMat = doubleImgTmp(Rect(j * 8, i * 8, 8, 8));
    			for (int r = 0; r < ROIMat.rows; r++)
    			{
    				for (int c = 0; c < ROIMat.cols; c++)
    				{
    					//8*8块中,后四行置0
    					if (r > 4)
    					{
    						ROIMat.at<double>(r, c) = 0.0;
    					}
    				}
    			}
    		}
    	}
    	doubleImgTmp.convertTo(ucharImgTmp, CV_8U);
    	imshow("regionalCodingImg", ucharImgTmp);
    }
    void thresholdCoding(Mat doubleImgTmp)
    {
    	int rNum = doubleImgTmp.rows / 8;
    	int cNum = doubleImgTmp.cols / 8;
    	double medianNumTmp = 0;
    	Mat ucharImgTmp;
    	Mat ROIMat = Mat(8, 8, CV_64FC1);	//用于分块处理的时候在原图像上面移动
    	for (int i = 0; i < rNum; i++)
    	{
    		for (int j = 0; j < cNum; j++)
    		{
    			ROIMat = doubleImgTmp(Rect(j * 8, i * 8, 8, 8));
    			medianNumTmp = get_medianNum(ROIMat);
    			for (int r = 0; r < ROIMat.rows; r++)
    			{
    				for (int c = 0; c < ROIMat.cols; c++)
    				{
    					if (abs(ROIMat.at<double>(r, c)) < 0)
    					{
    						ROIMat.at<double>(r, c) = 0;
    					}
    				}
    			}
    		}
    	}
    	doubleImgTmp.convertTo(ucharImgTmp, CV_8U);
    	imshow("thresholdCodingImg", ucharImgTmp);
    }
    double get_medianNum(Mat & imageROI)	//获取矩阵的中值
    {
    	vector<double> vectorTemp;
    	double tmpPixelValue = 0;
    	for (int i = 0; i < imageROI.rows; i++)	//将感兴趣区域矩阵拉成一个向量
    	{
    		for (int j = 0; j < imageROI.cols; j++)
    		{
    			vectorTemp.push_back(abs(imageROI.at<double>(i, j)));
    		}
    	}
    	for (int i = 0; i < vectorTemp.size() / 2; i++)	//进行排序
    	{
    		for (int j = i + 1; j < vectorTemp.size(); j++)
    		{
    			if (vectorTemp.at(i) > vectorTemp.at(j))
    			{
    				double temp;
    				temp = vectorTemp.at(i);
    				vectorTemp.at(i) = vectorTemp.at(j);
    				vectorTemp.at(j) = temp;
    			}
    		}
    	}
    	return vectorTemp.at(vectorTemp.size() / 2 - 1);	//返回中值
    }

    效果图如下:

  • (3)、图像JPEG的压缩:

  • 所用到的库函数:

    CV_EXPORTS_W Mat imdecode( InputArray buf, int flags );

    程序如下:

    /*JPEG图片压缩*/
    double getPSNR(Mat& src1, Mat& src2, int bb = 0);
    void ImageCompression_JPEG(Mat &src)
    {
    	//Mat src = imread("lena.png");
    	cout << "origin image size: " << src.dataend - src.datastart << endl;
    	cout << "height: " << src.rows << endl << "width: " << src.cols << endl << "depth: " << src.channels() << endl;
    	cout << "height*width*depth: " << src.rows*src.cols*src.channels() << endl << endl;
    	//(1) jpeg compression
    	vector<uchar> buff;//buffer for coding
    	vector<int> param = vector<int>(2);
    	param[0] = CV_IMWRITE_JPEG_QUALITY;
    	param[1] = 1;//default(95) 0-100
    	imencode(".jpg", src, buff, param);
    	cout << "coded file size(jpg): " << buff.size() << endl;//fit buff size automatically.
    	Mat jpegimage = imdecode(Mat(buff), CV_LOAD_IMAGE_COLOR);
    	//(2) png compression
    	param[0] = CV_IMWRITE_PNG_COMPRESSION;
    	param[1] = 8;//default(3)  0-9.
    	imencode(".png", src, buff, param);
    	cout << "coded file size(png): " << buff.size() << endl;
    	Mat pngimage = imdecode(Mat(buff), CV_LOAD_IMAGE_COLOR);
    	//(3) intaractive jpeg compression
    	char name[64];
    	namedWindow("jpg");
    	int q = 95;
    	createTrackbar("quality", "jpg", &q, 100);
    	int key = 0;
    	while (key != 'q')
    	{
    		param[0] = CV_IMWRITE_JPEG_QUALITY;
    		param[1] = q;
    		imencode(".jpg", src, buff, param);
    		Mat show = imdecode(Mat(buff), CV_LOAD_IMAGE_COLOR);
    		double psnr = getPSNR(src, show);//get PSNR
    		double bpp = 8.0*buff.size() / (show.size().area());//bit/pixe;
    		sprintf_s(name, "quality:%03d, %.1fdB, %.2fbpp", q, psnr, bpp);
    		putText(show, name, Point(15, 50), FONT_HERSHEY_SIMPLEX, 1, CV_RGB(255, 255, 255), 2);
    		imshow("jpg", show);
    		key = waitKey(33);
    		if (key == 's')
    		{
    			//(4) data writing 
    			sprintf_s(name, "q%03d_%.2fbpp.png", q, bpp);
    			imwrite(name, show);
    			sprintf_s(name, "q%03d_%.2fbpp.jpg", q, bpp);
    			param[0] = CV_IMWRITE_JPEG_QUALITY;
    			param[1] = q;
    			imwrite(name, src, param);;
    		}
    	}
    }
    double getPSNR(Mat& src1, Mat& src2, int bb)
    {
    	int i, j;
    	double sse, mse, psnr;
    	sse = 0.0;
    	Mat s1, s2;
    	cvtColor(src1, s1, CV_BGR2GRAY);
    	cvtColor(src2, s2, CV_BGR2GRAY);
    	int count = 0;
    	for (j = bb; j < s1.rows - bb; j++)
    	{
    		uchar* d = s1.ptr(j);
    		uchar* s = s2.ptr(j);
    		for (i = bb; i < s1.cols - bb; i++)
    		{
    			sse += ((d[i] - s[i])*(d[i] - s[i]));
    			count++;
    		}
    	}
    	if (sse == 0.0 || count == 0)
    	{
    		return 0;
    	}
    	else
    	{
    		mse = sse / (double)(count);
    		psnr = 10.0*log10((255 * 255) / mse);
    		return psnr;
    	}
    }

    效果图如下:

  • 主函数如下:

    #pragma warning(disable:4996)
    #include "opencv2/opencv.hpp"
    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "iostream"
    #include "fstream"
    #include "regex"
    #include "string.h"
    #include "time.h"
    #include "Windows.h"
    #include "math.h"
    #include "stdio.h"
    #define WINDOW_NAME  "(【程序窗口1】)"
    #define MAX_CHARS 256//最大字符数
    #define MAX_SIZE 1000000
    #define M_PI 3.141592653
    using namespace cv;
    using namespace std;
    const double EPS = 1e-12;
    const double PI = 3.141592653;
    
    class tmpNode
    {
    public:
    	unsigned char uch;//取值范围是0~255,用来表示一个8位的字符
    	unsigned long freq = 0;//字符出现的频率
    };
    int main()
    {
    	//Mat img = imread("3.jpg", CV_LOAD_IMAGE_GRAYSCALE);
    	//Mat img = imread("3.jpg", 1);
    	//Mat img = imread("3.jpg",0);
    	//Mat img = imread("1.jpg");
        //
    	//Mat img_1 = imread("p3-01-00_Huffman_Decode.bmp");
    	//Mat differenceimage;
    	//absdiff(img, img_1, differenceimage);
    	/*if (img.empty())
    	{
    		std::cout << "图片读取失败!" << "\n";
    		return -1;
    	}*/
    
    	//namedWindow("【原始图像】", CV_WINDOW_AUTOSIZE);
    	//imshow("【原始图像】", img);
    
    	//namedWindow("【压缩后解码图像】", CV_WINDOW_AUTOSIZE);
    	//imshow("【压缩后解码图像】", img_1);
    	
    	/*计算直方图判断噪声并且选择相应的滤波器进行滤波*/
    	//Mat dstImage = GaussianBlurfilterfunction(img);
    	//Mat dstImage = Blurefilterfunction(img);
    	//Mat dstImage = MedianBlurfilterfunction(img);
    	//Mat dstImage = BilateralBlurfilterfunction(img);
    	//Mat dstImage = Inversefiltering(img);
    	//namedWindow("【逆滤波图像】", CV_WINDOW_AUTOSIZE);
    	//imshow("【逆滤波图像】", dstImage);
    	//Mat img1 = img.clone();
    	//Drawing_one_dimensionalhistogram(img1);
    	//Otsusegmentation(img);
    	//Graphcutfunction(img);
    	//Watershedfunction(img);
    	//ImageCompression();
    	//ImageCompression_DCT(img);
    	//ImageCompression_JPEG(img);
    	//Huffmanfunction();
    	//Mat dstImg = FrequencyDomainGaussFiltering(img);
    	//Drawing_one_dimensionalhistogram(dstImg);
    	//grathcutfunctiong(img);
    	//namedWindow("【原始图像】", CV_WINDOW_AUTOSIZE);
    	//imshow("【原始图像】", img);
    	
    	//namedWindow("【差值图像】", CV_WINDOW_AUTOSIZE);
    	//imshow("【差值图像】", differenceimage);
    	waitKey(0);
    	//destroyAllWindows();
    	return 0;
    }

    完。

猜你喜欢

转载自blog.csdn.net/qq_40598185/article/details/85556821