1. 程式人生 > >DCT變換及量化的c++實現(基於opencv矩陣運算)

DCT變換及量化的c++實現(基於opencv矩陣運算)

由於DCT的數學原理不好描述,直接放程式碼了:

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

using namespace std;
using namespace cv;
const double pi = 3.141592;
void initDctMat(Mat &A)  //計算8x8塊的離散餘弦變換系數
{
	for (int i = 0; i < 8; ++i)
		for (int j = 0; j < 8; ++j)
		{
			float a;
			if (i == 0)
				a = sqrt(1.0 / 8.0);
			else
				a = sqrt(2.0 / 8.0);
			A.ptr<float>(i)[j] = a*cos((j + 0.5)*pi*i / 8);
		}
}
//dct變換
void myDct(Mat &image, const Mat &A, const Mat &mask)
{
	//分塊 8x8
	for(int i = 0; i < 256; i+=8)
		for (int j = 0; j < 256; j += 8)
		{

			//X = AXAT 
			image(Range(i, i + 8), Range(j, j + 8)) = A * image(Range(i, i + 8), Range(j, j + 8)) *A.t();
			//用mask量化
			image(Range(i, i + 8), Range(j, j + 8)) /= mask;
		
		}
}
//dct反變換
void myiDct(Mat &image, const Mat &A, const Mat &mask)
{
	//分塊8x8
	for (int i = 0; i < 256; i += 8)
		for (int j = 0; j < 256; j += 8)
		{
			//還原量化
			image(Range(i, i + 8), Range(j, j + 8)) = image(Range(i, i + 8), Range(j, j + 8)).mul(mask);
			//X = ATXA
			image(Range(i, i + 8), Range(j, j + 8)) = A.t() * image(Range(i, i + 8), Range(j, j + 8)) * A;
		}
}
int main()
{
	//讀取影象,影象為灰度圖,單通道
	Mat image = imread("lena.bmp",CV_LOAD_IMAGE_GRAYSCALE);
	Mat fimage;
	Mat A(Size(8, 8), CV_32FC1);// 離散餘弦係數矩陣
	//初始化mask量化矩陣
	float msk[8][8] = { {16,11,10,16,24,40,51,61},{12,12,14,19,26,58,60,55},{14,13,16,24,40,57,69,56},{14,17,22,29,51,87,80,62},{18,22,37,56,68,109,103,77},{24,35,55,64,81,104,113,92},{49,64,78,87,103,121,120,101},{72,92,95,98,112,100,103,99}};
	Mat mask(8, 8, CV_32FC1, msk);
	//顯示原圖
	if (!image.empty())
		imshow("image", image);
	//計算A係數
	initDctMat(A);
	//轉換成浮點數矩陣,進行dct變換
	image.convertTo(fimage, CV_32FC1);
	myDct(fimage, A, mask);
	//計算壓縮率, 用非零矩陣點數量比總數量、
	fimage.convertTo(image, CV_8UC1);
	double dctRate = countNonZero(image) / (256.0 * 256.0);
	cout << "the size becomes " << dctRate * 100 << "% of the original." << endl;
	imshow("壓縮圖", fimage);
	//dct反變換
	myiDct(fimage, A, mask);
	//顯示還原的影象
	fimage.convertTo(image, CV_8UC1);
	imshow("還原圖", image);
	waitKey(0);
	return 0;
}
執行結果: