应用C++实现opencv的dct图像压缩及分块图像压缩dctmtx
文章目录
实验三:图像处理基础与图像变换
实验目的:
通过本实验加深对数字图像的理解,熟悉MATLAB中的有关函数;应用DCT对图像进行变换;熟悉图像常见的统计指标,实现图像几何变换的基本方法。
实验内容:
(1)选择两幅图像,读入图像并显示,同时使用Matlab计算图像的大小、灰度平均值、协方差矩阵、灰度标准差和相关系数。
读入图像并显示
>> I1 = imread('C:\Users\XINGYE\Desktop\test.png');
>> I2 = imread('C:\Users\XINGYE\Desktop\test1.png');
>> figure;
>> subplot(1,2,1),subimage(I1);
>> title('test');
>> subplot(1,2,2),subimage(I2);
>> title('test1');
使用Matlab计算图像的大小、灰度平均值、协方差矩阵、灰度标准差和相关系数
① 计算图像大小
使用size()函数
>> %计算图片大小
>> size(I1)
ans =
2500 2500 3
>> size(I2)
ans =
2160 2184 3
>>
② 计算图像灰度平均值
先将两幅图像转换为相同的大小
>> I1 = imread('C:\Users\XINGYE\Desktop\test.png');
>> I1=imresize(I1,[2000,2000]);
>> I2 = imread('C:\Users\XINGYE\Desktop\test1.png');
>> I2=imresize(I2,[2000,2000]);
再将两张彩色图像转换为灰度图代码如下
Rgb2gray函数将rgb转换为灰度图=>对RGB三个分量分别乘以一定权重
>> i1=rgb2gray(I1);
>> i2=rgb2gray(I2);
>> figure;
>> subplot(2,2,1),subimage(I1);
>> title('彩色图像1');
>> subplot(2,2,2),subimage(I2);
>> title('彩色图像2');
>> subplot(2,2,3),subimage(i1);
>> title('灰度图1');
>> subplot(2,2,4),subimage(i2);
>> title('灰度图2');
>> %对i1求灰度平均值
>> i1=double(i1);%将uint8型转换为double型,否则不能计算统计量
>> [m,n]=size(i1);
>> s=0;
>> for x=1:m
for y=1:n
s=s+i1(x,y);%求像素值总和s
end
end
>> %求灰度平均值
>> value=s/m/n
value =
52.2798
>> %对i2求灰度平均值
i2=double(i2);%将uint8型转换为double型,否则不能计算统计量
[m,n]=size(i2);
s=0;
for x=1:m
for y=1:n
s=s+i2(x,y);%求像素值总和s
end
end
%求灰度平均值
value2=s/m/n
value2 =
93.6372
两图的灰度均值分别为52.2798和93.6372
法二直接用average1=mean2(i1);average2=mean2(i2);公式进行求解
③ 计算协方差矩阵
>> I1 = imread('C:\Users\XINGYE\Desktop\test.png');
I1=imresize(I1,[2000,2000]);
I2 = imread('C:\Users\XINGYE\Desktop\test1.png');
I2=imresize(I2,[2000,2000]);
i1=rgb2gray(I1);
i2=rgb2gray(I2);
>> i1=double(i1);
>> i2=double(i2);
>> %协方差矩阵
>> ans=cov(i1,i2)
ans =
1.0e+03 *
4.5678 -0.7341
-0.7341 3.6704
>>
④ 灰度标准差
>> standard1=std2(i1)
standard1 =
67.5858
>> standard2=std2(i2)
standard2 =
60.5836
灰度标准差分别为 67.5858和60.5836
⑤ 相关系数
>> i1=double(i1);
>> i2=double(i2);
>> xishu= corrcoef(i1,i2)
xishu =
1.0000 -0.1793
-0.1793 1.0000
-0.1793 1.0000Matlab
(2)DCT变换
RGB = imread('C:\Users\XINGYE\Desktop\test.png');
I = rgb2gray(RGB);
figure(1);
imshow(I);
J = dct2(I);
figure(2);
imshow(log(abs(J)),[]), colormap(jet(64)), colorbar
观察实验结果,说明figure2中图像色彩变换的规律,并写出其原因。
图像色彩变换规律及原因:
原始图像为彩色RGB图像,通过rgb2gray(RGB);函数将其转换为了灰度图,因此最开始显示出的图像为灰度图;接着对灰度图像进行离散余弦变换(其原理如下),在显示imshow(log(abs(J)),则是灰色的经过离散余弦变换的图片,最后使用colormap(jet(64))进行颜色映射算法,将灰度值映射成JET模式的伪彩。
每张图的每个像素都可以用余弦函数来表示形成频域编码,离散余弦变换是将这部分频域编码进行变换,将低频信号放在左上角,将低频信号放在右下角,(低频信号的能量比较低,高频信号的能量比较高,在图像中低频信号描述了图像的主要部分,高频信号描述了图像剧烈变化的部分,去掉一些高频信号对图像的影响不是很大)
经过dct2变换后的图片可以再经过逆dct函数idct2对图像进行重建
(3)DCT图像压缩
I=imread('cameraman.tif');
I=im2double(I);
T=dctmtx(8);
B=blkproc(I,[8 8],'P1*x*P2',T,T'); % 用 D×A×D’ 来计算离散余弦变换
mask=[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];
B2=blkproc(B,[8 8],'P1.*x',mask); % 系数选择
I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 离散余弦逆变换
subplot(1,2,1),imshow(I);
subplot(1,2,2),imshow(I2);
① 运行代码,观察实验结果,写出图像压缩的基本原理。
实验结果为将原始图像进行压缩
Mask为取舍矩阵
B=blkproc(I,[8 8],’P1*x*P2′,T,T’); % 用 T*A*T’ 来计算离散余弦变换
B2=blkproc(B,[8 8],’P1.*x’,mask); % 系数选择
I2=blkproc(B2,[8 8],’P1*x*P2′,T’,T); % 离散余弦逆变换
原理:先把图像分为 8×8 块,然后用 dctmtx 分别对每个子图像进行离散余弦变换,对变换系数仅保留左上角的 10 个值 (4+3+2+1),然后对这 10 个系数进行离散余弦逆变换重新得到各个子图像,再用 dctmtx 进行重构。
② 计算原图像和压缩后图像的差(相减)
③ 修改mask矩阵中非0元素的个数,写出修改后的压缩的图像和原图像之间的差和非0元素个数的关系,并比较变换后系数的重要性
分别用保留15个系数,10个系数和3个系数的mask进行比较
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];
通过以上实验结果对比图片可知,只保留左上角部分系数,图像还原效果并未受大的影响。对于图像来说,其中的边缘、细节上的信号往往是变化比较快,比较剧烈的。所以,图像中的高频信号对应的是图像中的细节和边缘信息。而图像中的大部分内容信息,往往是变换缓慢、频率低的信号。所以,信号中的低频信号对应主要的图像信息。左上角为低频数据,右下角为高频数据,并且数据能量主要集中在低频区域,即低频区域的值比较大,高频区域的值较小。也就是因为高频区域值小,能量低,所以我们可以在接下来的压缩中,将他们置0,在图片质量看起来影响不大的情况下完成压缩。
左上角的1越多保留的细节越多,相反左上角的0越少保留的细节越少,就越模糊
。
③ 使用别的图像进行上述实验,验证结论正确与否
(4)使用C++与OpenCV实现(3)的图像压缩
对于大小不是8的倍数的图像进行扩大
Mat change_img_size(int width,int height,Mat dgsrc){
//形状扩充
int adjust_width = width;
if(width % 8 != 0){
adjust_width = adjust_width + 8 - (width % 8);
}
int adjust_height = height;
if(height % 8 != 0){
adjust_height = adjust_height + 8 - (height % 8);
}
cout<<"expand img width:"<<adjust_width<<" height:"<<adjust_height<<endl;
Mat big_dgsrc = Mat(adjust_height, adjust_width, CV_64FC1, 0.0);
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
big_dgsrc.at(i, j) = dgsrc.at(i, j);
}
}
return big_dgsrc;
}
① 灰色图像的分离
void grey_main(){
//读取原图片
Mat img = imread("./test.png");
if (img.empty())
{
cout << "没有读取到图像" << endl;
return ;
}
width = img.size().width;
height = img.size().height;
cout << width << "\t" << height << endl;
//转换为灰度图
Mat I;
cvtColor(img, I, COLOR_BGR2GRAY); // I = rgb2gray(RGB);
//获取图像长和宽
Mat T(8, 8, CV_64FC1); // 离散余T弦系数矩阵
//建立8*8的DCT变换矩阵
dctmtx(T); // T=dctmtx(8);
//转换成浮点矩阵
Mat fudianimg;
I.convertTo(fudianimg, CV_64FC1);
//进行分块TT
Mat B = blkproc(fudianimg, T); //分块 // B=blkproc(I,[8 8],'P1*x*P2',T,T'); % 用 D×A×D’ 来计算离散余弦变换
Mat bigimg = change_img_size(width, height, fudianimg); //图像扩大为8的倍数
//系数取舍矩阵,保留10个系数
double mask[8][8] = {// mask取舍矩阵
{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}}; // 4+3+2+1
//系数选择
Mat B2 = mblkproc(bigimg, mask); //系数选择 // B2=blkproc(B,[8 8],'P1.*x',mask); % 系数选择
//离散余弦逆变换
Mat I2 = reblkproc(bigimg, T); //离散余弦逆变换 //I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 离散余弦逆变换
//显示图像
imshow("原图像", I);
imshow("分块DCT后的压缩图", B2);
imshow("恢复后的图像", I2);
cout << I2.size();
//图像恢复
Rect I2_size = Rect(0, 0, width, height);
I2 = I2(I2_size);
cout << I2.size();
imshow("原图与压缩后图像的差", I-I2); //代表失真程度
waitKey();
}
② 彩色的图像分离
void rgb_main(){
//读取原图片
// 读入图像
Mat src = imread("./test.png");
if (src.empty())
{
printf("不能找到文件。\n");
return ;
}
//获取图像长和宽
width = src.size().width;
height = src.size().height;
vector mv;
split(src, mv);
// imshow("blue channel", mv[0]);
// imshow("green channel", mv[1]);
// imshow("red channel", mv[2]);
//转换为灰度图
Mat R, G, B;
R = mv[2];
G = mv[1];
B = mv[0];
vector dct_RGB(3);
Mat dct_R = dct(R);
Mat dct_G = dct(G);
Mat dct_B = dct(B);
//此处顺序不可错,否则合并后的压缩图像不为原来的彩色
dct_RGB[0] = dct_B;
dct_RGB[1] = dct_G;
dct_RGB[2] = dct_R;
//合并
Mat dst;
merge(dct_RGB, dst);
imwrite("./result.jpg", dst);
imshow("合并压缩RGB图像", dst);
//显示图像
imshow("原图像", src);
imshow("R", dct_R);
imshow("G", dct_G);
imshow("B", dct_B);
imshow("失真程度原图减压缩", src-dst);
waitKey();
}
注意:图像合并时存储图像的顺序需为BGR,刚好与RGB颜色相反,其为imread的默认存储颜色空间顺序,否则合成的图像如下
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include
#include
#define PI acos(-1)
using namespace std;
using namespace cv;
//全局变量图像宽和高
int width, height;
Mat change_img_size(int width,int height,Mat dgsrc){
//形状扩充
int adjust_width = width;
if(width % 8 != 0){
adjust_width = adjust_width + 8 - (width % 8);
}
int adjust_height = height;
if(height % 8 != 0){
adjust_height = adjust_height + 8 - (height % 8);
}
cout<<"expand img width:"<<adjust_width<<" height:"<<adjust_height<<endl;
Mat big_dgsrc = Mat(adjust_height, adjust_width, CV_64FC1, 0.0);
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
big_dgsrc.at(i, j) = dgsrc.at(i, j);
}
}
return big_dgsrc;
}
// T=dctmtx(8); 生成一个8*8 DCT变换矩阵
void dctmtx(Mat T)
{ // DCT离散余弦变换变换公式
for (int u = 0; u < T.rows; ++u)
{
for (int v = 0; v < T.cols; ++v)
{
double a;
if (u == 0) //当u=0时,a(u)=sqrt(1/N)
{
a = sqrt(1.0 / T.rows);
}
else //当u=1,2,...,N-1时。a(u)=sqrt(2/N)
{
a = sqrt(2.0 / T.rows);
}
T.at(u, v) = a * cos((2 * v + 1) * PI * u / (2 * T.rows)); // 2-D的DCT变换公式
}
}
}
// B=blkproc(I,[8 8],'P1*x*P2',T,T'); 进行分块,'P1*x*P2'来计算离散余弦变换
Mat blkproc(Mat image,Mat A)
{
Mat temp = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
//分块
for (int i = 0; i < height / 8; i++)
{
for (int j = 0; j < width / 8; j++)
{
temp = image(Rect(j * 8, i * 8, 8, 8));
// X = AXA'
temp = A * temp * A.t();
}
}
Mat B ;
image.convertTo(B, CV_8UC1);
return B;
}
// B2=blkproc(B,[8 8],'P1.*x',mask); % 系数选择
Mat mblkproc(Mat image,double mask[8][8])
{
//分块后生成对应的变换图
// B2=blkproc(B,[8 8],'P1.*x',mask); % 系数选择
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (height - i < 8 || width - j < 8)
{
continue;
}
//对每一块进行系数选择
if (mask[j % 8][i % 8] == 0)
{
image.at(i, j) = 0.0;
}
}
}
Mat B2;
image.convertTo(B2, CV_8UC1);
return B2;
}
// I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 离散余弦逆变换
Mat reblkproc(Mat image, Mat A)
{
Mat temp = Mat(8, 8, CV_64FC1); // 8*8矩阵,通道为1
for (int i = 0; i < height / 8; i++)
{
for (int j = 0; j < width / 8; j++)
{
temp = image(Rect(j * 8, i * 8, 8, 8));
temp = A.t() * temp * A;
}
}
Mat I2;
image.convertTo(I2, CV_8UC1);
return I2;
}
Mat dct(Mat I){
Mat T(8, 8, CV_64FC1); // 离散余T弦系数矩阵
//建立8*8的DCT变换矩阵
dctmtx(T); // T=dctmtx(8);
//转换成浮点矩阵
Mat fudianimg;
I.convertTo(fudianimg, CV_64FC1);
//进行分块TT
Mat B = blkproc(fudianimg, T); //分块 // B=blkproc(I,[8 8],'P1*x*P2',T,T'); % 用 D×A×D’ 来计算离散余弦变换
//系数取舍矩阵,保留10个系数
double mask[8][8] = {// mask取舍矩阵
{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}}; // 4+3+2+1
//系数选择
Mat B2 = mblkproc(fudianimg, mask); //系数选择 // B2=blkproc(B,[8 8],'P1.*x',mask); % 系数选择
//离散余弦逆变换
Mat I2 = reblkproc(fudianimg, T); //离散余弦逆变换 //I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 离散余弦逆变换
return I2;
}
void grey_main(){
//读取原图片
Mat img = imread("./test.png");
if (img.empty())
{
cout << "没有读取到图像" << endl;
return ;
}
width = img.size().width;
height = img.size().height;
cout << width << "\t" << height << endl;
//转换为灰度图
Mat I;
cvtColor(img, I, COLOR_BGR2GRAY); // I = rgb2gray(RGB);
//获取图像长和宽
Mat T(8, 8, CV_64FC1); // 离散余T弦系数矩阵
//建立8*8的DCT变换矩阵
dctmtx(T); // T=dctmtx(8);
//转换成浮点矩阵
Mat fudianimg;
I.convertTo(fudianimg, CV_64FC1);
//进行分块TT
Mat B = blkproc(fudianimg, T); //分块 // B=blkproc(I,[8 8],'P1*x*P2',T,T'); % 用 D×A×D’ 来计算离散余弦变换
//图像扩大为8的倍数
Mat bigimg = change_img_size(width, height, fudianimg); //图像扩大为8的倍数
//系数取舍矩阵,保留10个系数
double mask[8][8] = {// mask取舍矩阵
{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}}; // 4+3+2+1
//系数选择
Mat B2 = mblkproc(bigimg, mask); //系数选择 // B2=blkproc(B,[8 8],'P1.*x',mask); % 系数选择
//离散余弦逆变换
Mat I2 = reblkproc(bigimg, T); //离散余弦逆变换 //I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 离散余弦逆变换
//显示图像
imshow("原图像", I);
imshow("分块DCT后的压缩图", B2);
imshow("恢复后的图像", I2);
cout << I2.size();
//图像恢复
Rect I2_size = Rect(0, 0, width, height);
I2 = I2(I2_size);
cout << I2.size();
imshow("原图与压缩后图像的差", I-I2); //代表失真程度
waitKey();
}
void rgb_main(){
//读取原图片
// 读入图像
Mat src = imread("./test.png");
if (src.empty())
{
printf("不能找到文件。\n");
return ;
}
//获取图像长和宽
width = src.size().width;
height = src.size().height;
vector mv;
split(src, mv);
// imshow("blue channel", mv[0]);
// imshow("green channel", mv[1]);
// imshow("red channel", mv[2]);
//转换为灰度图
Mat R, G, B;
R = mv[2];
G = mv[1];
B = mv[0];
vector dct_RGB(3);
Mat dct_R = dct(R);
Mat dct_G = dct(G);
Mat dct_B = dct(B);
//此处顺序不可错,否则合并后的压缩图像不为原来的彩色
// 图像合并时存储图像的顺序需为BGR,刚好与RGB颜色相反,其为imread的默认存储颜色空间顺序
dct_RGB[0] = dct_B;
dct_RGB[1] = dct_G;
dct_RGB[2] = dct_R;
//合并
Mat dst;
merge(dct_RGB, dst);
imwrite("./result.jpg", dst);
imshow("合并压缩RGB图像", dst);
//显示图像
imshow("原图像", src);
imshow("R", dct_R);
imshow("G", dct_G);
imshow("B", dct_B);
imshow("失真程度原图减压缩", src-dst);
waitKey();
}
int main()
{
grey_main();
// rgb_main();
return 0;
}
(5)编写程序实现图像的水平垂直和中心对称(注:不能用系统的旋转函数)
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include
#include
using namespace std;
using namespace cv;
int width, height;//图像宽和高
void horizontal_translate(Mat img)
{
//深拷贝原矩阵
Mat horizontal_img = img.clone();
//进行水平对称
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
horizontal_img.at(i, j) = img.at(i, width - j - 1);
}
}
//显示水平对称图像
imshow("水平对称处理", horizontal_img);
}
void vertical_translate(Mat img)
{
Mat vertical_img = img.clone();
//进行垂直对称
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
vertical_img.at(i, j) = img.at(height - i - 1, j);
}
}
//显示垂直对称图像
imshow("垂直对称处理", vertical_img);
}
void center_translate(Mat img)
{
Mat center_img = img.clone();
//进行中心对称
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
center_img.at(i, j) = img.at(height - i - 1, width - j - 1);
}
}
//显示中心对称图像
imshow("中心对称处理", center_img);
}
int main()
{
//读取
Mat img = imread("./test.png");
if (img.empty())
{
cout << "没有读取到图像" << endl;
return -1;
}
//获取宽高
width = img.size().width;
height = img.size().height;
//显示
imshow("原图像", img);
//水平
horizontal_translate(img);
//垂直
vertical_translate(img);
//中心
center_translate(img);
//保持显示状态
waitKey(0);
return 0;
}
遇到问题:没有拷贝原图像矩阵直接操作会出错,
解决方案:反转前对原图像矩阵进行深拷贝
微信赞赏 支付宝赞赏