2019-11-23 13:57:35 qq_37286676 阅读数 148

 

以下是原创的C++代码,可在VC6.0及以上版本测试,相关头文件若缺失请在百度或谷歌下载。

#include <stdio.h>  
#include "BmpRot.h"  
#include "stdlib.h"  
#include "math.h"  
#include <iostream> 
#include <cmath>  
 //#include "stdafx.h" 
#define pi 3.14159//圆周率宏定义  
#define LENGTH_NAME_BMP 30//bmp图片文件名的最大长度  
#define num 8
float s[8][8];
float t[8][8];
int n=1,u,v,cv,cu;
using namespace std;  

//变量定义  
BITMAPFILEHEADER strHead;  
RGBQUAD strPla[256];//256色调色板  
BITMAPINFOHEADER strInfo;  
 
//显示位图文件头信息  
void showBmpHead(BITMAPFILEHEADER pBmpHead){  
    cout<<"位图文件头:"<<endl;  
    cout<<"文件大小:"<<pBmpHead.bfSize<<endl;  
    cout<<"保留字_1:"<<pBmpHead.bfReserved1<<endl;  
    cout<<"保留字_2:"<<pBmpHead.bfReserved2<<endl;  
    cout<<"实际位图数据的偏移字节数:"<<pBmpHead.bfOffBits<<endl<<endl;  
}  
  
void showBmpInforHead(tagBITMAPINFOHEADER pBmpInforHead){  
    cout<<"位图信息头:"<<endl;  
    cout<<"结构体的长度:"<<pBmpInforHead.biSize<<endl;  
    cout<<"位图宽:"<<pBmpInforHead.biWidth<<endl;  
    cout<<"位图高:"<<pBmpInforHead.biHeight<<endl;  
    cout<<"biPlanes平面数:"<<pBmpInforHead.biPlanes<<endl;  
    cout<<"biBitCount采用颜色位数:"<<pBmpInforHead.biBitCount<<endl;  
    cout<<"压缩方式:"<<pBmpInforHead.biCompression<<endl;  
    cout<<"biSizeImage实际位图数据占用的字节数:"<<pBmpInforHead.biSizeImage<<endl;  
    cout<<"X方向分辨率:"<<pBmpInforHead.biXPelsPerMeter<<endl;  
    cout<<"Y方向分辨率:"<<pBmpInforHead.biYPelsPerMeter<<endl;  
    cout<<"使用的颜色数:"<<pBmpInforHead.biClrUsed<<endl;  
    cout<<"重要颜色数:"<<pBmpInforHead.biClrImportant<<endl;  
}  


void dct(float data[num][num],float output[num][num])
{
	double cu,cv;
	short u=0;
	short v=0;
	short i=0;
	short j=0;
	for(u = 0;u < num;u++)
	{
		for(v = 0;v < num;v++)
		{
			if(u==0)
				cu = sqrt(1.0/2.0);
			else
				cu = 1;
			if(v==0)
				cv = sqrt(1.0/2.0);
			else
				cv = 1;
			double tmp=0.0;
			for(i = 0;i < num;i++)
			{
				for(j = 0;j < num;j++)
				{
					tmp+= (data[i][j])
						* cos( (2*i+1) * u * pi / (2.0*num) )
						* cos( (2*j+1) * v * pi / (2.0*num) );

				}
			}
			output[u][v]= ( cu * cv /4.0) * tmp;
			if(output[u][v]<10 && output[u][v]>-10 )
			{output[u][v]=0;}
		}

	}
          
}


void idct(float input[num][num],float output[num][num])
{
	short i,j,u,v;
	double cu,cv;
	for(i = 0;i < num;i++)
	{
		for(j = 0;j < num;j++)
		{
			double tmp=0.0;
			for(u = 0;u < num;u++)
			{
				for(v = 0;v < num;v++)
				{
					if(u==0)
						cu = sqrt(1.0/2.0);
					else
						cu = 1;
					if(v==0)
						cv = sqrt(1.0/2.0);
					else
						cv = 1;
					tmp+= (cu*cv/4.0)  * (input[u][v])
						* cos( (2*i+1) * u * pi / (2.0*num))
						* cos( (2*j+1) * v * pi / (2.0*num));
				}
			}
			output[i][j]=tmp;
		}
	}
}


int main(){  
    char strFile[LENGTH_NAME_BMP];//bmp文件名  
    IMAGEDATA *imagedata = NULL;//动态分配存储原图片的像素信息的二维数组  
    IMAGEDATA *imagedataRot = NULL;//动态分配存储旋转后的图片的像素信息的二维数组  
	IMAGEDATA *imagedataRot1 = NULL;
	IMAGEDATA *imagedataRot2 = NULL;
    int width,height,temp;//图片的宽度和高度
   
	cout<<"请输入所要读取的文件名:"<<endl;   
	cin>>strFile;  
    FILE *fpi,*fpw;  
    fpi=fopen(strFile,"rb");  
    if(fpi != NULL){  
        //先读取文件类型  
        WORD bfType;  
        fread(&bfType,1,sizeof(WORD),fpi);  
        if(0x4d42!=bfType)  
        {  
            cout<<"the file is not a bmp file!"<<endl;  
            return NULL;  
        }  
        //读取bmp文件的文件头和信息头  
        fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi);  
        //showBmpHead(strHead);//显示文件头  
        fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi);  
        //showBmpInforHead(strInfo);//显示文件信息头  
  
        //读取调色板  
        for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)  
        {  
            fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fpi);  
            fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fpi);  
            fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fpi);  
            fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fpi);  
        }  
  
        width = strInfo.biWidth;  
        height = strInfo.biHeight;  
        //图像每一行的字节数必须是4的整数倍  
        width = (width * sizeof(IMAGEDATA) + 3) / 4 * 4;  
        //imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));  
        imagedata = (IMAGEDATA*)malloc(width * height);  
        imagedataRot = (IMAGEDATA*)malloc( width  * height * sizeof(IMAGEDATA));  
		imagedataRot1 =  (IMAGEDATA*)malloc(width * height); 
		imagedataRot2 =  (IMAGEDATA*)malloc(width * height); 
        //初始化原始图片的像素数组  
        for(int i = 0;i < height;++i)  
        {  
            for(int j = 0;j < width;++j)  
            {  
                (*(imagedata + i * width + j)).blue = 0;  
                //(*(imagedata + i * width + j)).green = 0;  
                //(*(imagedata + i *  width + j)).red = 0;  
            }  
        }  
        //初始化旋转后图片的像素数组  
        for( i = 0;i <  height;++i)  
        {  
            for(int j = 0;j <  width;++j)  
            {  
                (*(imagedataRot + i *  width + j)).blue = 0;  
                //(*(imagedataRot + i *  width + j)).green = 0;  
                //(*(imagedataRot + i *  width + j)).red = 0;  
            }  
        }  
		
        //fseek(fpi,54,SEEK_SET);  
        //读出图片的像素数据  
        fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);  
        fclose(fpi);  
    }  
    else  
    {  
        cout<<"file open error!"<<endl;  
        return NULL;  
    }  
  
    int i,j,x,y;
float a[8][8]={0.0};
	for(int m = 0;m < 64;m++)
	{	
		for(int n = 0;n < 64;n++)
		{	
			for(i = 0;i < 8;i++)
			{	for(j = 0;j < 8;j++)
				{
                   a[i][j]=(float)(*(imagedata + (m*8+i) *  width + (n*8+j))).blue;   
				}
			}
	dct(a,s);
	for(x = 0;x < 8;x++)
			{
				for(y = 0;y < 8;y++)
				{
					if( x+y >=4 )
						s[x][y]=0;
				}
			}
	idct(s,t);
      for(i = 0;i < 8;i++)
			{
				for(j = 0;j < 8;j++)
				{
					(*(imagedataRot + (m*8+i )*  width + n*8+j)).blue=(unsigned char)t[i][j]; 
				}
			}
		}
    }
    //保存bmp图片  
    if((fpw=fopen("b.bmp","wb"))==NULL)  
    {  
        cout<<"create the bmp file error!"<<endl;  
        return NULL;  
    }  
    WORD bfType_w=0x4d42;  
    fwrite(&bfType_w,1,sizeof(WORD),fpw);  
    //fpw +=2;  
    fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw);  
    strInfo.biWidth = width;  
    strInfo.biHeight =  height;  
    fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw);  
    for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)  
    {  
        fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpw);  
        fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpw);  
        fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpw);  
        fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fpw);  
    }  
    //保存像素数据  
    for(i =0;i <  height;++i)  
    {  
        for(int j = 0;j <  width;++j)  
        {  
            fwrite( &((*(imagedataRot + i *width + j)).blue),1,sizeof(BYTE),fpw);  
            //fwrite( &((*(imagedataRot + i *  width + j)).green),1,sizeof(BYTE),fpw);  
            //fwrite( &((*(imagedataRot + i * width + j)).red),1,sizeof(BYTE),fpw);  
        }  
    }  
    fclose(fpw);     
   
    //释放内存  
    delete[] imagedata;  
    delete[] imagedataRot;  

}   

Matlab版本会在后面更新,敬请期待!Matlab效果如下:

2017-03-03 09:01:52 qq_34784753 阅读数 10551

DCT变换,也就是离散余弦变换(Discrete Cosine Transform)是图像频域变换的一种,实际上可以看成是一种空域的低通滤波器,DCT也可以看做是傅里叶变换的一种特殊情况。在傅里叶级数中,如果被展开的函数是实偶函数,那么在傅里叶级数中则只包含余弦项,再将其离散化,由此便可导出离散余弦变化。

目前,离散余弦变换以及它的改进算法已经成为广泛应用于信号处理和图像处理,特别是用于图像压缩和语音压缩编解码的重要工具和技术。这是由于DCT具有很强的“能量集中”的特性,大多数的自然信号(包括声音和图像)的能量都集中在离散余弦变换后的低频部分,DCT的作用是吧图像中点和点间的规律呈现出来,虽然DCT本身并没有压缩作用,但是却为以后压缩时的“取舍”奠定了必不可少的基础。

离散余弦变换在某种程度上与离散傅里叶变换有写类似,但是与DFT不同的是DCT只使用实数部分,DFT需要计算的是复数而非实数,而进行复数运算通常比计算实数运算费时得多,所以DCT相当于一个长度是其二被的DFT,从形式上看,DCT是一个线性的可逆函数。

下面先简单介绍一下DCT的数学基础

一维的DCT的正变换公式如下:




其中,F(u)是第u个DCT变换的系数,f(x)是时域中的N点的序列,x=0,1,2...,N-1

二维图像的DCT变换形式为:


其中,f(x,y)为空域中的二维向量,x=0,1,2....M-1;y = 0,1,2...,N-1;F(u,v)为变换系数矩阵

使用OpenCV进行简单的实现程序如下:

//实现二维图像的DCT

#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;
using namespace std;

int main()
{
	
	Mat srcImage = imread("2345.jpg");
	if (!srcImage.data)
	{
		cout << "读入图像出错" << endl;
		return -1;
	}
	imshow("原图像", srcImage);
	int height = srcImage.rows;
	int width = srcImage.cols;

	//从BGR空间转换到YUV颜色空间
	Mat yuvImage(srcImage.size(), CV_8UC3);
	cvtColor(srcImage, yuvImage, CV_BGR2YUV);
	//定义输出图像
	Mat dstImage(srcImage.size(), CV_64FC3);

	//分割YUV三个通道
	vector<Mat> channels;
	split(yuvImage, channels);

	//提取YUV颜色各个通道的值
	Mat y = channels.at(0);	imshow("Y", y);
	Mat u = channels.at(1); imshow("U", u);
	Mat v = channels.at(2); imshow("V", v);

	//定义DCT系数的三个通道
	Mat DCTY(srcImage.size(), CV_64FC1);
	Mat DCTU(srcImage.size(), CV_64FC1);
	Mat DCTV(srcImage.size(), CV_64FC1);

	//进行DCT变换
	dct(Mat_<double>(y), DCTY);
	dct(Mat_<double>(u), DCTU);
	dct(Mat_<double>(v), DCTV);

	channels.at(0) = Mat_<uchar>(DCTY);
	channels.at(1) = Mat_<uchar>(DCTU);
	channels.at(2) = Mat_<uchar>(DCTV);

	merge(channels,dstImage);

	imshow("DCT图像", dstImage);

	waitKey();
	return 0;
}






2017-01-15 15:02:25 zhaomengszu 阅读数 2176
转自:http://blog.csdn.net/ahafg/article/details/48808443
DCT又称离散余弦变换,是一种块变换方式,只使用余弦函数来表达信号,与傅里叶变换紧密相关。常用于图像数据的压缩,通过将图像分成大小相等(一般为8*8)的块,利用DCT对其进行变换,得到更加简洁的数据。因为图像像素间存在较大的空间相关性,DCT可以大大减小这些相关性,使图像能量集中在左上角区域,从而利于数据压缩。变换后得到的数据称为DCT系数。这一过程是无损的。

二维DCT变换

这里来看看二维DCT变换的公式:


 

clear; clc; I = [12,23,53,16;42,16,68,45;34,62,73,26;72,15,34,28]; %数据块 A = zeros(4); %变换矩阵A,也可以通过函数dctmtx(n)求得 for i = 0:3 for j = 0:3 if i == 0 a = sqrt(1/4); else a = sqrt(2/4); end A(i+1,j+1) = a*cos((j+0.5)*pi*i/4) end end D = A*I*A'; %DCT变换 D1 = dct2(I); %matlab DCT函数进行DCT变换 D2 = A'*D*A; %DCT逆变换


 由结果可以看出,D,D1方式得到的DCT系数相同,说明矩阵形式的DCT变换公式是正确的,D2的数据与原数据I相同,实现了数据恢复。

另外通过运行函数dctmtx(4)可以发现得到的变换矩阵与A完全相同。

Matlab 函数实现

matlab实现离散余弦变换有两种方法:

  1. 一种为函数dct2( ), 使用函数dct2,该函数用一个基于FFT的算法来提高当输入较大的方阵时的计算速度。
  2. 另一种为函数dctmtx( ), 使用由dctmtx函数返回的DCT变换矩阵,这种方法较适合于较小的输入方阵(例如8×8或16×16)。
  3. . 函数:dct2( )

    实现图像的二维离散余弦变换。调用格式为: 
    B = dct2(A) 
    B = dct2(A,[M N]) 
    B = dct2(A,M,N) 
    式中A表示要变换的图像,M和N是可选参数,表示填充后的图像矩阵大小,B表示变换后得到的图像矩阵。其逆变换函数为idct2( ); 

  4. I = imread('1_1.jpg');%输入灰度图像 D = dct2(I); %DCT变换 D1 = idct2(D); %逆变换 subplot(1,2,1);imshow(I); subplot(1,2,2);imshow(uint8(D1));

    2. 函数:dctmtx( )

    D = dctmtx(N) 
    式中D是返回N×N的DCT变换矩阵,如果矩阵A是N×N方阵,则A的DCT变换可用D×A×D’来计算。这在有时比dct2计算快,特别是对于A很大的情况。上面有提到过。

    对于图像的DCT变换,这里还需用到一个函数blkproc( ),其功能为对图像分块进行DCT变换。 
    blkproc( )定义如下: 
    B = blkproc(A,[M N],Fun) ,A为输入图像,M*N为块大小,Fun为处理函数 
    常用的方式为: 
    B = blkproc(A,[8,8],’P1*x*P2’,T,T’); T为变换矩阵,P1和P2为参数,代表T*x*T’ 。

  5. I = imread('1_1.jpg'); %输入灰度图像 I = im2double(I); D = dctmtx(8); C = blkproc(I,[8,8],'P1*x*P2',D,D'); %D'为D的转置 mask1=[1 1 1 1 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]; mask2=[1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]; mask3=[1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]; X = blkproc(C,[8,8],'P1.*x',mask1); %保留15个系数 I1 = blkproc(X,[8,8],'P1*x*P2',D',D); %重构图像 X2 = blkproc(C,[8,8],'P1.*x',mask2); %保留10个系数 I2 = blkproc(X2,[8,8],'P1*x*P2',D',D); %重构图像 X3 = blkproc(C,[8,8],'P1.*x',mask3); %保留3个系数 I3 = blkproc(X3,[8,8],'P1*x*P2',D',D); %重构图像 subplot(2,4,1);imshow(I); subplot(2,4,2);imshow(I1); subplot(2,4,3);imshow(I2); subplot(2,4,4);imshow(I3);

    上面代码中,通过求得图像DCT系数,利用mask等矩阵对其进行量化,保留左上角主要的系数值,对于右下角的值由于其为非常小的高频系数,量化去除后对于图像的质量影响不大,可以利用这一性质对图像进行压缩处理。

    保留系数越多则图像压缩质量越好,下面比较几幅图像质量,从左到右分别为原图,mask1,mask2,mask3;


  6.  

2017-01-12 13:08:06 RachelKong 阅读数 15711

实验三 图像的DCT变化及量化

一、问题描述

利用matlab,将road.tif彩色图像的分辨率转换为256*256,将图片转化为double数据类型,再利用T=dctmtx(8)建立一个8*8的DCT变换矩阵。将图像I划分为多个8*8的图像块B,对每一个图像块B进行DCT变换(D=T*B*T’),接着对结果采用四种量化方案:只保留直流系数;保留直流系数和前9个交流系数;保留直流系数和前35个交流系数;保留直流系数和前53个交流系数。再对这四种方案分别进行逆DCT变化(I2=T’*D*T)。最后显示出原图像I,重构后的图像I2,和差值I-I2。

二、问题分析

JPEG压缩标准中,需要对图像作DCT变换,这一步能够对图片进行压缩,同时,根据量化系数的不同,压缩质量也不同。

DCT变换,即离散余弦变换,是一种广泛应用的变换编码方法,它能够以数据无关的方式解除输入信号之间的相关性,因而应用广泛。在进行变换时,首先将图片分割为8*8的小块,分别对小块进行各自的DCT变换。变换的矩阵可调用函数dctmtx,得到余弦基函数作为变换基T。T*B*T’是变换的操作,接着对矩阵块进行量化。根据人眼对低频信号更为敏感的特性,建立合适的量化矩阵(存在广为接受的版本),接着对矩阵的每个元素进行除以对应的量化值。接着按照实验要求,对DCT系数进行遮罩,建立四个不同的遮罩矩阵,分别对左上角赋值为0,对小块的元素进行四种量化方案。这样就完成了DCT变换。进行逆变换时,直接用T’*B*T得到结果矩阵,转化为图像形式,得到最终的结果。需要注意的是,在对RGB图像进行DCT变换时,需要对三个颜色通道分别进行操作,因为DCT变换不是三维的。此外,在逆DCT变换之后,需要将每个通道的矩阵数值转化为uint8格式,再利用cat函数合并三个通道,得到重构的图像以及差值图像。

其实,在JPEG编码格式中,将RGB转化为YUV颜色空间,再对通道进行DCT变换,而不是直接对RGB三个通道进行变换,这是因为人眼对色度的敏感度低于对灰度的敏感度。

三、算法分析与详细设计

DCT基的第一个系数为恒定的直流系数,其余为AC系数,按照要求构建四种遮罩矩阵:

mask0=[1 0 0 0 0 0 0 0

             0 0 0 0 0 0 0 0

             0 0 0 0 0 0 0 0

             0 0 0 0 0 0 0 0

             0 0 0 0 0 0 0 0

             0 0 0 0 0 0 0 0

             0 0 0 0 0 0 0 0

             0 0 0 0 0 0 0 0];

 mask9=[1 1 1 1 0 0 0 0

              1 1 1 0 0 0 0 0

              1 1 0 0 0 0 0 0

              1 0 0 0 0 0 0 0

              0 0 0 0 0 0 0 0

              0 0 0 0 0 0 0 0

              0 0 0 0 0 0 0 0

              0 0 0 0 0 0 0 0];

  mask35=[1 1 1 1 1 1 1 1

                 1 1 1 1 1 1 1 0

                 1 1 1 1 1 1 0 0

                 1 1 1 1 1 0 0 0

                 1 1 1 1 0 0 0 0

                 1 1 1 0 0 0 0 0

                 1 1 0 0 0 0 0 0

                 1 0 0 0 0 0 0 0];

   mask53=[1 1 1 1 1 1 1 1

                  1 1 1 1 1 1 1 1

                  1 1 1 1 1 1 1 1

                  1 1 1 1 1 1 1 1

                  1 1 1 1 1 1 1 0

                  1 1 1 1 1 1 0 0

                  1 1 1 1 1 0 0 0

                  1 1 1 1 0 0 0 0];

 

调用彩色图像DCT变换的函数,参数为RGB图像和遮罩矩阵。

亮度量化表可以乘以一个系数,用来控制压缩率。系数越大,压缩率越高,得到的图像质量越差。遮罩矩阵保留的AC系数越多,得到的图像质量越高。

function [ RGB_rec,miss ] = ColorDCTtransform( RGB,mask )

%进行彩色图像的DCT变换

返回重构的图像

 

%亮度量化表

m=0.5*[ 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];

%RGB图分层处理  得到3个分量图

R = RGB(:,:,1);

G = RGB(:,:,2);

B = RGB(:,:,3);

%转换为双精度

IR = double(R);

IG = double(G);

IB = double(B);

%建立8*8的DCT变换矩阵

T=dctmtx(8);

%进行DCT变换

RR = blkproc(IR,[8,8],'P1*x*P2',T,T');

GG = blkproc(IG,[8,8],'P1*x*P2',T,T');

BB = blkproc(IB,[8,8],'P1*x*P2',T,T');

 

%量化

LR = blkproc(RR,[8 8], 'round(x./P1)',m);

LG = blkproc(GG,[8 8], 'round(x./P1)',m);

LB = blkproc(BB,[8 8], 'round(x./P1)',m);

%对DCT系数进行遮罩处理

mR=blkproc(LR,[8 8],'x.*P1.*P2',mask,m);

mG=blkproc(LG,[8 8],'x.*P1.*P2',mask,m);

mB=blkproc(LB,[8 8],'x.*P1.*P2',mask,m);

 

%反DCT变化 IDCT

YR =blkproc(mR,[8 8],'P1*x*P2',T',T);

YG =blkproc(mG,[8 8],'P1*x*P2',T',T);

YB =blkproc(mB,[8 8],'P1*x*P2',T',T);

 

%转换为uint8

YR = uint8(YR);

YG = uint8(YG);

YB = uint8(YB);

 

%计算重构图像和差值

RGB_rec =cat(3,YR,YG,YB);

miss=RGB-RGB_rec;

 

end

灰度图像的处理类似,只是不需要对三个通道进行操作。

四、实验结果分析

实验结果如下,分别是灰度图像和彩色RGB图像的DCT变换结果,可以看到,保留的AC系数越多,得到的图像质量越好,与原图的差值越小。这是因为高频信号也进行了一定的保留,量化误差较小,因此逆DCT变换得到的图像质量也较好。图像的质量损失产生于量化过程,量化系数是根据人眼对不同亮度的敏感程度进行设计的,左上角数值小,右下角数值大,这样高频信息损失大一些,人眼很难有所察觉,低频信号损失小,对人眼观察造成的影响较小。

灰度图像的实验结果。

彩色图像的实验结果。

使用第一种遮罩矩阵、也就是只保留直流系数的矩阵,量化损失最大,逆变换后得到的图像能够看到明显的锯齿,保留9个AC系数时,质量变高,但仍能看到一些锯齿。保留35个AC系数和53个AC系数得到的结果非常相似,人眼几乎看不出差别,误差图像也几乎为全黑,证明了人眼确实对高频信号敏感度很低。

DCT变换本身不会对图片的质量造成影响,大小不会改变,但是量化的过程会给图片质量带来损失,量化矩阵的系数大小也影响着压缩率,值越大,压缩率越高,得到的图片质量也越低,相应地,图片所占的空间也较小。保留35个系数已经能够较好地还原图片,肉眼很难辨识出区别。

五、实验总结

通过本次实验,了解了DCT变换的基本原理和过程和量化矩阵对图片质量产生的影响,分别对灰度图和RGB图进行了DCT变换,其中彩色图像需要对三个通道分别进行变换。如果想要得到更好的效果,可以先将RGB颜色转化到YUV或YIQ空间(并进行二次采样,对亮度值全采样,其余的进行半采样),因为人眼对亮度的敏感度远高于对色度的敏感度。这也是JPEG图像压缩的原理。

同时,除了对原理的了解,还熟悉了matlab的函数使用,能够进行简单的图像分块,调用DCT变换矩阵的函数,和对小块进行量化操作使用的blkproc函数。

附资源链接:点我

DCT算法的原理和优化

阅读数 32522

没有更多推荐了,返回首页