数字信号与图像处理实验三:图像处理基础与图像变换
实验目的
通过本实验加深对数字图像的理解,熟悉MATLAB中的有关函数;应用DCT对图像进行变换;熟悉图像常见的统计指标,实现图像几何变换的基本方法。
实验内容:
-
选择两幅图像,读入图像并显示,同时使用Matlab计算图像的大小,灰度平均值、协方差矩阵、灰度标准差和相关系数。
-
DCT变换
RGB = imread('autumn.tif'); I = rgb2gray(RGB); figure(1); imshow(I); J = dct2(I); figure(2); imshow(log(abs(J)),[]), colormap(jet(64)), colorbar
观察实验结果,说明figure2中图像色彩变换的规律,并写出其原因。
-
DCT图像压缩
I=imread('cameraman.tif'); I=im2double(I); T=dctmtx(8); B=blkproc(I,[8 8],'P1*x*P2',T,T'); 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矩阵中非0元素的个数,写出修改后的压缩的图像和原图像之间的差和非0元素个数的关系,并比较变换后系数的重要性
④ 使用别的图像进行上述实验,验证结论正确与否
-
使用C++与OpenCV实现(3)的图像压缩
-
编写程序实现图像的水平垂直和中心对称(注:不能用系统的旋转函数)
实验要求
- 熟悉MATLAB的图像处理函数
- 熟练掌握java/C#/C/C++的图像处理方法
- 完成程序代码编写
实验过程
What is DCT
维基百科——DCT
离散余弦变换(DCT for Discrete Cosine Transform),是与傅里叶变换相关的一种变换,它类似于离散傅里叶变换(DFT),但是只使用实数。
离散余弦变换,尤其是它的第二种类型,经常被信号处理和图像处理使用,用于对信号和图像(包括静止图像和运动图像)进行有损数据压缩。这是由于离散余弦变换具有很强的"能量集中"特性:大多数的自然信号(包括声音和图像)的能量都集中在离散余弦变换后的低频部分,而且当信号具有接近**马尔科夫过程**(Markov processes)的统计特性时,离散余弦变换的去相关性接近于K-L变换(Karhunen-Loève 变换–它具有最优的去相关性)的性能。
一维DCT变换
一维DCT变换共有八种形式,其中最常用的是第二种形式,由于其运算简单、适用范围广。
f(i)
:原始信号
F(u)
:DCT变换后的系数
N
:原始信号的点数
c(u)
:可以认为是一个补偿系数,可以使DCT变换矩阵为正交矩阵
二维DCT变换
MATLAB用到的函数
-
dct2()
函数 —— 二维离散余弦变换 -
colormap()
:查看并设置当前颜色图。详解参考
传参:
map
—— 新颜色方案的颜色图。例如colormap(jet(64))
将当前图窗的颜色图设置为从jet颜色图中选择的64种颜色。
下面列出预定义的颜色图:
-
colorbar
:在当前坐标区或图的右侧显示一个垂直颜色栏。颜色栏显示当前颜色图并指示数据值到颜色图的映射。 -
blkproc(A, [m,n], fun, parameter1, parameter2, ……)
[m, n]
:图像以m*n为分块单位,对图像进行处理fun
:应用此函数分别对每个分块单位的像素进行处理parameter1, parameter2。。。
:fun函数的参数
p1.图像基本操作
读取图像
% 图像读取
I1 = imread('实验三\wallhaven-vqmqrm_1920x1080.png');
I2 = imread('实验三\wallhaven-zyxz9v_1920x1080.png');
读取图像大小
% 图像大小
sizeI1 = size(I1);
sizeI2 = size(I2);
转灰度图
% RGB转为灰度图
grayI1 = rgb2gray(I1);
grayI2 = rgb2gray(I2);
计算灰度图均值
% 计算灰度图均值
aveGrayI1 = mean2(grayI1);
aveGrayI2 = mean2(grayI2);
计算标准差
v1 = std2(I1);
v2 = std2(I2);
计算相关系数
灰度图和中值滤波处理后的图像,计算两者相关系数
% 计算相关系数
I11 = medfilt2(grayI1); % 中值滤波
r1 = corr2(grayI1, I11);
计算协方差
% 计算协方差
% 函数im2double 将其值归一化到[0,1]之间
dI1 = im2double(grayI1);
dI2 = im2double(grayI2);
cov1 = cov(dI1, dI2);
显示图像
% 显示图像
subplot(1,2,1);imshow(grayI1);title('灰度图');
subplot(1,2,2);imshow(I11);title('中值滤波');
p2. DCT变换
灰度图
DCT变换
如图,使用dct2函数对灰度图像执行二维DCT。使用对数刻度显示变换后的图像,可以观察到,大部分能量在左上角。
源码
clc
clear
close all
% 图像读取
I1 = imread('实验三\wallhaven-vqmqrm_1920x1080.png');
% I2 = imread('实验三\wallhaven-zyxz9v_1920x1080.png');
% 转灰度图
grayI1 = rgb2gray(I1);
figure(1);
imshow(grayI1);
% dct2
J = dct2(grayI1);
figure(2);
imshow(log(abs(J)),[]);
colormap(jet(64));
colorbar;
% idct2
K = idct2(J);
K = rescale(K);
figure(3);
montage({grayI1, K});
title('Original Grayscale Image (Left) and Processed Image (Right)');
% , colormap(jet(64)), colorbar
p3. DCT图像压缩
示例源码
I=imread('实验三\wallhaven-zyxz9v_1920x1080.png');
I=rgb2gray(I);
I=im2double(I);
T=dctmtx(8); % 生成一个8*8 DCT变换矩阵
B=blkproc(I,[8 8],'P1*x*P2',T,T'); % x就是每一个分成的8*8大小的块,P1=T,P2=T'
% P1*x*P2相当于像素块的处理函数
% 也就是fun=P1*x*P2=T*x*T' 功能是进行DCT
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]; % 保留左上角10个系数
B2=blkproc(B,[8 8],'P1.*x',mask); % 舍弃每个块中的高频系数,达到图像压缩的目的
I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 进行反余弦变换,得到压缩后的图象
figure(1);imshow(I);title('原图');
figure(2);imshow(I2);title('经过IDCT变换后得到的重构图像');
% 注意:如果是三维图象(如RGB)则要先将图像转换成图像变成灰度图象或者用reshape函数转换维数
① 运行代码,观察实验结果,写出图像压缩的基本原理。
运行结果如下:
figure1
figure2
压缩原理
先将要压缩的图像分割为一定的8×8像素的图像子块,再用DCT把子块变为8×8的DCT系数阵;
然后用一个8×8的量化值阵列(mask)对这些系数进行量化;最后用熵编码器将量化后的系数编码成一串比特数据流.经过传输或存储,比特数据流经过熵解码器进行解码,重新生成一组量化了的DCT系数,使用与编码时相同的量化值阵列对这些量化了的系数进行反量化,利用反向离散余弦变换(IDCT)将此8×8DCT系数阵列变换成空间域的8×8图像子块,最后将反变换后的组合成一幅图像.这样完成了一幅图像的压缩与解压过程。
② 计算原图像和压缩后图像的差(相减)
figure(3);imshow(abs(I-I2));title('原图像与重构图像之差,反应失真程度');
figure3
可以看到,原图与重构图像素之间差值很小,为黑色。
③ 修改mask矩阵中非0元素的个数,写出修改后的压缩的图像和原图像之间的差和非0元素个数的关系,并比较变换后系数的重要性
实验结果如下:
通过以上实验结果对比图片可知,只保留左上角部分系数,图像还原效果并未受大的影响。
由上个DCT变换中可以注意到,图像DCT系数能量集中在左上方的低频区域,对于一些高频数据,将其置为0后,图像复原并未受较大影响。
对于图像来说,其中的边缘、细节上的信号往往是变化比较快,比较剧烈的。所以,图像中的高频信号对应的是图像中的细节和边缘信息。而图像中的大部分内容信息,往往是变换缓慢、频率低的信号。所以,信号中的低频信号对应主要的图像信息。这样,我们就可以通过DCT,求出对应的不同频域分量,最后只保留低频数据,舍弃高频数据,就可以达到压缩的目的了。
例如一个8乘8的图像块,其DCT变换后的数据,左上角为低频数据,右下角为高频数据,并且数据能量主要集中在低频区域,即低频区域的值比较大,高频区域的值较小。也就是因为高频区域值小,能量低,所以我们可以在接下来的压缩中,将他们置0,在图片质量看起来影响不大的情况下完成压缩。
在这里,我们需要理解一下,为什么低频数据的值就比较大,而高频数据的值比较小呢?首先,对于获得的基函数,低频数值高于高频数值。在设定u=0时,其此时的基函数的取值都等于1,都为正数,如果源信号与此基函数频率相同或相近,那么他们相乘得到的系数将都是相对较大的值,在设定u=1时,其此时的基函数的取值大概为09,0.8,0.5,0.2,-0.2,-0.5,-0.8,-0.9有正数有负数,且绝对值都小于1,那么,对于一个与此信号频率相同的源信号,相乘后的结果较u=0时要小。所以在低频域内得到较大的数值。并且对于一个8*8的图像块非负数据,其最左上角的那个DC值,等于8乘上源信号的平均值;再者,在一般的图像中,低频数据量高于高频数据量,一幅图片中大多数数据,不论是亮度或者是色度,变化都不会很剧烈,所以,低频区域值较大,所占能量更高。
源码:
mask1=[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
0 0 0 0 0 0 0 0];
mask2=[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];
mask3=[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];
mask4=[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];
mask5=[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];
mask6=[0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 1 1 1 1 1
0 0 1 1 1 1 1 1
0 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1];
mask7=[0 0 0 0 0 0 1 1
0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 1 1 1 1 1
0 0 1 1 1 1 1 1
0 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1];
mask8=[0 0 0 0 0 1 1 1
0 0 0 0 1 1 1 1
0 0 0 1 1 1 1 1
0 0 1 1 1 1 1 1
0 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];
mask9=[0 0 0 0 1 1 1 1
0 0 0 1 1 1 1 1
0 0 1 1 1 1 1 1
0 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];
mask10=[0 0 0 1 1 1 1 1
0 0 1 1 1 1 1 1
0 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
1 1 1 1 1 1 1 1];
B2 = blkproc(B,[8 8],'P1.*x',mask1);
I2 = blkproc(B2,[8 8],'P1*x*P2',T',T);
B3 = blkproc(B,[8 8],'P1.*x',mask2);
I3 = blkproc(B3,[8 8],'P1*x*P2',T',T);
B4 = blkproc(B,[8 8],'P1.*x',mask3);
I4 = blkproc(B4,[8 8],'P1*x*P2',T',T);
B5 = blkproc(B,[8 8],'P1.*x',mask4);
I5 = blkproc(B5,[8 8],'P1*x*P2',T',T);
B6 = blkproc(B,[8 8],'P1.*x',mask5);
I6 = blkproc(B6,[8 8],'P1*x*P2',T',T);
B7 = blkproc(B,[8 8],'P1.*x',mask6);
I7 = blkproc(B7,[8 8],'P1*x*P2',T',T);
B8 = blkproc(B,[8 8],'P1.*x',mask7);
I8 = blkproc(B8,[8 8],'P1*x*P2',T',T);
B9 = blkproc(B,[8 8],'P1.*x',mask8);
I9 = blkproc(B9,[8 8],'P1*x*P2',T',T);
B10 = blkproc(B,[8 8],'P1.*x',mask9);
I10 = blkproc(B10,[8 8],'P1*x*P2',T',T);
B11 = blkproc(B,[8 8],'P1.*x',mask10);
I11 = blkproc(B11,[8 8],'P1*x*P2',T',T);
figure(1);
subplot(3,2,1);imshow(I);title('原图');
subplot(3,2,2);imshow(I2);title('mask保留左上角6个系数');
subplot(3,2,3);imshow(I3);title('mask保留左上角3个系数');
subplot(3,2,4);imshow(I4);title('mask保留左上角1个系数');
subplot(3,2,5);imshow(I5);title('mask保留左上角15个系数');
subplot(3,2,6);imshow(I6);title('mask保留左上角36个系数');
figure(2);
subplot(3,2,1);imshow(I);title('原图');
subplot(3,2,2);imshow(I7);title('mask保留右下角36个系数');
subplot(3,2,3);imshow(I8);title('mask保留右下角43个系数');
subplot(3,2,4);imshow(I9);title('mask保留右下角49个系数');
subplot(3,2,5);imshow(I10);title('mask保留右下角54个系数');
subplot(3,2,6);imshow(I11);title('mask保留右下角58个系数');
④ 使用别的图像进行上述实验,验证结论正确与否
实验序号:2
实验序号:3
P4.OpenCV C++代码复现
Mat相关
Mat类的成员函数
col(int i) //获取第i列数据,返回也是Mat类型
row(int j) //获取第j行数据,返回也是Mat类型
size() //矩阵尺寸[rows, cols]
total() //rows* cols,而非矩阵元素数
channels() //图像通道数
rowRange()和colRange() // 读取连续几行或者几列
m.rowRange(2,4)
遍历像素函数at()
格式为:myMat.at<float>(i, j) 获取矩阵第i行第j列的元素
获取矩阵行首元素指针ptr(),格式为mymat.ptr<int>(i) 指向第i行首元素的指针
m.isContiuous()每一行是否连续存储的。
多通道Mat访问
# 用at进行多通道访问,对应的数据类型如下:
typedef Vec<uchar, 2> Vec2b; //无符号双通道 CV_8U:0~255
typedef Vec<uchar, 3> Vec3b; //无符号3通道
typedef Vec<uchar, 4> Vec4b;
typedef Vec<short, 2> Vec2s; //short型双通道 CV_16S:-32768~32767
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;
typedef Vec<int, 2> Vec2i; //整型双通道 CV_32S:-2147483648~2147483647
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<float, 2> Vec2f; //浮点型双通道 CV_32F:1.1810-38~3.401038
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
typedef Vec<double, 2> Vec2d; //double型双通道 CV_64F:2.2310-308~1.7910308
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;
OpenCV三通道图像Mat遍历
cout<<"Mat src:"<<endl;
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
cout<<""<<src.at<Vec3d>(i, j)<<" ";
}
cout<<endl;
}
dctmtx()函数复现
根据一维DCT变换公式计算即可
void dctmtx8(Mat A){ // 8*8
for(int i=0; i<A.rows; ++i){
for(int j=0; j<A.cols; ++j){
double a;
if(i==0){
a = sqrt(1.0 / A.rows);
}else{
a = sqrt(2.0 / A.rows);
}
A.at<double>(i, j) = a * cos((j + 0.5) * M_PI * i / A.rows);
}
}
}
DCT变换 or IDCT
A * ROIMat * A'
IDCT只需修改一行计算公式即可
A' * ROIMat * A
void myDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
for(int i = 0; i < image.size().height/8; i++)
{
for (int j = 0; j < image.size().width/8; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
ROIMat = A * ROIMat * A.t(); // DCT
// ROIMat = A.t() * ROIMat * A; IDCT
}
}
}
mask 量化函数
// 量化 复现 B2=blkproc(B,[8 8],'P1.*x',mask); % 舍弃每个块中的高频系数,达到图像压缩的目的
void maskimg(Mat image){
for(int i = 0; i < image.size().height; i++)
{
for (int j = 0; j < image.size().width; j++)
{
if(mask[j%8][i%8]==0){
image.at<double>(i, j) = 0.0;
}
}
}
}
图像大小扩充至8的倍数(补零操作,最后再复原)
//形状扩充
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;
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<double>(i, j) = dgsrc.at<double>(i, j);
}
}
// 形状复原
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
dgsrc.at<double>(i, j) = big_dgsrc.at<double>(i, j);
}
}
多通道处理
BGR转YUV,分解为三通道,分别对各通道数据作DCT、量化、IDCT。
最后合并即可实现。
实验结果(灰度图DCT图像压缩)
实验结果(RGB图DCT图像压缩)
保留左上角10个系数
保留左上角6个系数
源码(dctimg.cpp)
#include<opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include<iostream>
#include<math.h>
#define M_PI 3.14159265358979323846
using namespace std;
using namespace cv;
int width,height;
double msk[8][8] = {
{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}};
void dctmtx8(Mat A){ // 生成一个8*8 DCT变换矩阵
for(int i=0; i<A.rows; ++i){
for(int j=0; j<A.cols; ++j){
double a;
if(i==0){
a = sqrt(1.0 / A.rows);
}else{
a = sqrt(2.0 / A.rows);
}
A.at<double>(i, j) = a * cos((j + 0.5) * M_PI * i / A.rows);
}
}
}
void myDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
for(int i = 0; i < height/8; i++)
{
for (int j = 0; j < width/8; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
//X = AXA'
ROIMat = A * ROIMat * A.t();
}
}
for(int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if(height - i < 8 || width -j < 8){
continue;
}
if(msk[j%8][i%8]==0){
image.at<double>(i, j) = 0.0;
}
}
}
}
Mat myIDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
Mat res;
for(int i = 0; i < height/8; i++)
{
for (int j = 0; j < width/8 ; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
ROIMat = A.t() * ROIMat * A;
}
}
image.convertTo(res, CV_8UC1);
return res;
}
int main(){
Mat src;
Mat graysrc;
Mat dgsrc;
src = imread("/home/luffy/digital/img/luffy14.jpg");
if (src.empty())
{
cout << "没有读取到图像" << endl;
return -1;
}
cvtColor(src, graysrc, COLOR_BGR2GRAY);
// width == cols
// height == rows;
width = src.size().width;
height = src.size().height;
cout<<"img width: "<<width<<" height: "<<height<<endl;
Mat A(8, 8, CV_64FC1);// 离散余弦系数矩阵
//基于8*8块的DCT变换及其反变换
//计算A系数
dctmtx8(A);
//转换成浮点数矩阵,进行dct变换
graysrc.convertTo(dgsrc, CV_64FC1);
myDct(dgsrc, A);
//计算压缩率, 用非零矩阵点数量比总数量、
// gsrc.convertTo(graysrc, CV_8UC1);
// double dctRate = countNonZero(graysrc) / (420.0 * 320.0);
// cout << "the size becomes " << dctRate * 100 << "% of the original." << endl;
// Rect windows(); //利用这个8*8的矩形来进行8*8块的DCT变换
imshow("原图像", graysrc);
imshow("DCT变化、量化后的压缩图", dgsrc);
Mat res = myIDct(dgsrc, A);
imshow("IDCT恢复后的图像", res);
waitKey(0);
return 0;
}
源码(rgbdctimg.cpp)
#include<opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include<iostream>
#include<math.h>
#define M_PI 3.14159265358979323846
using namespace std;
using namespace cv;
int width,height;
double msk[8][8] = {
{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}};
void dctmtx8(Mat A){ // 8*8
for(int i=0; i<A.rows; ++i){
for(int j=0; j<A.cols; ++j){
double a;
if(i==0){
a = sqrt(1.0 / A.rows);
}else{
a = sqrt(2.0 / A.rows);
}
A.at<double>(i, j) = a * cos((j + 0.5) * M_PI * i / A.rows);
}
}
}
void myDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
for(int i = 0; i < height/8; i++)
{
for (int j = 0; j < width/8; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
//X = AXA'
ROIMat = A * ROIMat * A.t();
}
}
for(int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if(height - i < 8 || width -j < 8){
continue;
}
if(msk[j%8][i%8]==0){
image.at<double>(i, j) = 0.0;
}
}
}
}
void myIDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
Mat res;
for(int i = 0; i < height/8; i++)
{
for (int j = 0; j < width/8 ; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
//X = AXA'
ROIMat = A.t() * ROIMat * A;
}
}
}
int main(){
Mat src;
src = imread("/home/luffy/digital/img/luffy14.jpg");
if (src.empty())
{
cout << "没有读取到图像" << endl;
return -1;
}
// width == cols
// height == rows;
width = src.size().width;
height = src.size().height;
cout<<"img width: "<<width<<" height: "<<height<<endl;
//分解为YUV颜色空间
Mat YUVImage;
cvtColor(src,YUVImage,COLOR_BGR2YUV);
//分解为三个通道
vector<Mat> YUV;
split(YUVImage,YUV);
//先转换下格式
Mat Y, U, V;
YUV[0].convertTo(Y,CV_64FC1);
YUV[1].convertTo(U,CV_64FC1);
YUV[2].convertTo(V,CV_64FC1);
Mat A(8, 8, CV_64FC1); // 离散余弦系数矩阵
//计算A系数
dctmtx8(A);
myDct(Y, A);
myDct(U, A);
myDct(V, A);
imshow("原图像", src);
imshow("DCT_Y", Y);
imshow("DCT_U", U);
imshow("DCT_V", V);
//计算压缩率, 用非零矩阵点数量比总数量、
// gsrc.convertTo(graysrc, CV_8UC1);
// double dctRate = countNonZero(graysrc) / (420.0 * 320.0);
// cout << "the size becomes " << dctRate * 100 << "% of the original." << endl;
// Rect windows(); //利用这个8*8的矩形来进行8*8块的DCT变换
myIDct(Y, A);
myIDct(U, A);
myIDct(V, A);
vector<Mat> YUV_dst(3);
//格式转换
Y.convertTo(YUV_dst[0],CV_8UC1);
U.convertTo(YUV_dst[1],CV_8UC1);
V.convertTo(YUV_dst[2],CV_8UC1);
//将三个通道进行合并
Mat yuv,dst_RGB;
merge(YUV_dst,yuv);
//转为RGB图像
cvtColor(yuv,dst_RGB,COLOR_YUV2BGR);
imshow("恢复后的图像", dst_RGB);
waitKey(0);
return 0;
}
源码(改)dctimg2.cpp
#include<opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include<iostream>
#include<math.h>
#define M_PI 3.14159265358979323846
using namespace std;
using namespace cv;
double mask[8][8] = {
{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}};
// 复现 T=dctmtx(8); 生成一个8*8 DCT变换矩阵
void dctmtx8(Mat A){
for(int i=0; i<A.rows; ++i){
for(int j=0; j<A.cols; ++j){
double a;
if(i==0){
a = sqrt(1.0 / A.rows);
}else{
a = sqrt(2.0 / A.rows);
}
A.at<double>(i, j) = a * cos((j + 0.5) * M_PI * i / A.rows);
}
}
}
//复现 I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 进行反余弦变换,得到压缩后的图象
void myDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
for(int i = 0; i < image.size().height/8; i++)
{
for (int j = 0; j < image.size().width/8; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
//X = AXA'
ROIMat = A * ROIMat * A.t();
}
}
}
// 量化 复现 B2=blkproc(B,[8 8],'P1.*x',mask); % 舍弃每个块中的高频系数,达到图像压缩的目的
void maskimg(Mat image){
for(int i = 0; i < image.size().height; i++)
{
for (int j = 0; j < image.size().width; j++)
{
if(mask[j%8][i%8]==0){
image.at<double>(i, j) = 0.0;
}
}
}
}
// 复现 I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 进行反余弦变换,得到解压缩后的图像
void myIDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
for(int i = 0; i < image.size().height/8; i++)
{
for (int j = 0; j < image.size().width/8 ; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
ROIMat = A.t() * ROIMat * A;
}
}
}
int main(){
Mat src, graysrc, big_dgsrc, dgsrc;
src = imread("/home/luffy/digital/img/luffy14.jpg");
if (src.empty())
{
cout << "没有读取到图像" << endl;
return -1;
}
cvtColor(src, graysrc, COLOR_BGR2GRAY); // 转为灰色图
// width == cols
// height == rows;
// 图像的宽、高度
int width = src.size().width;
int height = src.size().height;
cout<<"img width: "<<width<<" height: "<<height<<endl;
Mat A(8, 8, CV_64FC1);// 离散余弦系数矩阵
//基于8*8块的DCT变换及其反变换
//计算A系数
dctmtx8(A);
//转换成浮点数矩阵,进行dct变换
graysrc.convertTo(dgsrc, CV_64FC1);
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;
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<double>(i, j) = dgsrc.at<double>(i, j);
}
}
myDct(big_dgsrc, A);
maskimg(big_dgsrc);
//计算压缩率, 用非零矩阵点数量比总数量、
// gsrc.convertTo(graysrc, CV_8UC1);
// double dctRate = countNonZero(graysrc) / (420.0 * 320.0);
// cout << "the size becomes " << dctRate * 100 << "% of the original." << endl;
// Rect windows(); //利用这个8*8的矩形来进行8*8块的DCT变换
imshow("原图像", graysrc);
Mat res1;
big_dgsrc.convertTo(res1, CV_8UC1);
imshow("DCT变化、量化后的压缩图", res1);
myIDct(big_dgsrc, A);
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
dgsrc.at<double>(i, j) = big_dgsrc.at<double>(i, j);
}
}
Mat res2;
dgsrc.convertTo(res2, CV_8UC1);
imshow("IDCT恢复后的图像", res2);
waitKey(0);
return 0;
}
源码(改)rgbdctimg2.cpp
#include<opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include<iostream>
#include<math.h>
#define M_PI 3.14159265358979323846
using namespace std;
using namespace cv;
int width,height;
double mask[8][8] = {
{1,1,0,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},
{0,0,0,0,0,0,0,0}};
// 复现 T=dctmtx(8); 生成一个8*8 DCT变换矩阵
void dctmtx8(Mat A){
for(int i=0; i<A.rows; ++i){
for(int j=0; j<A.cols; ++j){
double a;
if(i==0){
a = sqrt(1.0 / A.rows);
}else{
a = sqrt(2.0 / A.rows);
}
A.at<double>(i, j) = a * cos((j + 0.5) * M_PI * i / A.rows);
}
}
}
//复现 I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 进行反余弦变换,得到压缩后的图象
void myDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
for(int i = 0; i < image.size().height/8; i++)
{
for (int j = 0; j < image.size().width/8; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
//X = AXA'
ROIMat = A * ROIMat * A.t();
}
}
}
// 量化 复现 B2=blkproc(B,[8 8],'P1.*x',mask); % 舍弃每个块中的高频系数,达到图像压缩的目的
void maskimg(Mat image){
for(int i = 0; i < image.size().height; i++)
{
for (int j = 0; j < image.size().width; j++)
{
if(mask[j%8][i%8]==0){
image.at<double>(i, j) = 0.0;
}
}
}
}
// 复现 I2=blkproc(B2,[8 8],'P1*x*P2',T',T); % 进行反余弦变换,得到解压缩后的图像
void myIDct(Mat image, Mat A){
Mat ROIMat = Mat(8, 8, CV_64FC1); //用于分块处理的时候在原图像上面移动
for(int i = 0; i < image.size().height/8; i++)
{
for (int j = 0; j < image.size().width/8 ; j++)
{
ROIMat = image(Rect(j * 8, i * 8, 8, 8));
ROIMat = A.t() * ROIMat * A;
}
}
}
int main(){
Mat src;
src = imread("/home/luffy/digital/img/luffy14.jpg");
if (src.empty())
{
cout << "没有读取到图像" << endl;
return -1;
}
// width == cols
// height == rows;
width = src.size().width;
height = src.size().height;
cout<<"img width: "<<width<<" height: "<<height<<endl;
//分解为YUV颜色空间
Mat YUVImage;
cvtColor(src,YUVImage,COLOR_BGR2YUV);
//分解为三个通道
vector<Mat> YUV;
split(YUVImage,YUV);
//转换为64位浮点数据类型
Mat Y, U, V;
YUV[0].convertTo(Y,CV_64FC1);
YUV[1].convertTo(U,CV_64FC1);
YUV[2].convertTo(V,CV_64FC1);
Mat A(8, 8, CV_64FC1); // 离散余弦系数矩阵
//计算A系数
dctmtx8(A);
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 Y1 = Mat(adjust_height, adjust_width, CV_64FC1, 0.0);
Mat U1 = Mat(adjust_height, adjust_width, CV_64FC1, 0.0);
Mat V1 = Mat(adjust_height, adjust_width, CV_64FC1, 0.0);
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
Y1.at<double>(i, j) = Y.at<double>(i, j);
}
}
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
U1.at<double>(i, j) = U.at<double>(i, j);
}
}
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
V1.at<double>(i, j) = V.at<double>(i, j);
}
}
myDct(Y1, A);
myDct(U1, A);
myDct(V1, A);
maskimg(Y1);
maskimg(U1);
maskimg(V1);
imshow("原图像", src);
imshow("DCT_Y通道", Y1);
imshow("DCT_U通道", U1);
imshow("DCT_V通道", V1);
myIDct(Y1, A);
myIDct(U1, A);
myIDct(V1, A);
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
Y.at<double>(i, j) = Y1.at<double>(i, j);
}
}
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
U.at<double>(i, j) = U1.at<double>(i, j);
}
}
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
V.at<double>(i, j) = V1.at<double>(i, j);
}
}
vector<Mat> YUV_dst(3);
//格式转换
Y.convertTo(YUV_dst[0],CV_8UC1);
U.convertTo(YUV_dst[1],CV_8UC1);
V.convertTo(YUV_dst[2],CV_8UC1);
//将三个通道进行合并
Mat yuv,dst_RGB;
merge(YUV_dst,yuv);
//转为RGB图像
cvtColor(yuv,dst_RGB,COLOR_YUV2BGR);
imshow("恢复后的图像", dst_RGB);
waitKey(0);
return 0;
}
P5 图像对称
主要图像处理代码
水平对称,分别对每行的数据水平翻转即可;
//水平对称处理 horizontal_src
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
horizontal_src.at<Vec3b>(i, j) = src.at<Vec3b>(i, width-j-1);
}
}
垂直对称,分别对每列的数据垂直翻转即可;
//垂直对称
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
vertical_src.at<Vec3b>(i, j) = src.at<Vec3b>(height-i-1, j);
}
}
中心对称,水平+垂直对称。文章来源:https://www.toymoban.com/news/detail-763678.html
//中心对称
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
center_src.at<Vec3b>(i, j) = src.at<Vec3b>(height-i-1, width-j-1);
}
}
运行效果如下图:
文章来源地址https://www.toymoban.com/news/detail-763678.html
源码
#include<opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include<iostream>
#include<math.h>
#define M_PI 3.14159265358979323846
using namespace std;
using namespace cv;
int width,height;
int main(){
Mat src, horizontal_src, vertical_src, center_src;
src = imread("/home/luffy/digital/img/luffy14.jpg");
if (src.empty())
{
cout << "没有读取到图像" << endl;
return -1;
}
// width == cols
// height == rows;
width = src.size().width;
height = src.size().height;
//深拷贝
horizontal_src = src.clone();
vertical_src = src.clone();
center_src = src.clone();
cout<<"img width: "<<width<<" height: "<<height<<endl;
cout<<"channel: "<<src.channels()<<endl;
cout<<"img processing ..."<<endl;
//水平对称处理 horizontal_src
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
horizontal_src.at<Vec3b>(i, j) = src.at<Vec3b>(i, width-j-1);
}
}
//垂直对称
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
vertical_src.at<Vec3b>(i, j) = src.at<Vec3b>(height-i-1, j);
}
}
//中心对称
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
center_src.at<Vec3b>(i, j) = src.at<Vec3b>(height-i-1, width-j-1);
}
}
cout<<"done !"<<endl;
imshow("原图像",src);
imshow("水平对称处理",horizontal_src);
imshow("垂直对称处理", vertical_src);
imshow("中心对称处理", center_src);
waitKey(0);
return 0;
}
到了这里,关于数字信号与图像处理实验三:图像处理基础与图像变换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!