Record study record life
实验三:图像处理基础与图像变换
实验三:图像处理基础与图像变换

实验三:图像处理基础与图像变换

应用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');

zDFoOU.png
使用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');

zDFIyT.png
分别对两张灰度图求灰度平均值

>> %对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);

①  运行代码,观察实验结果,写出图像压缩的基本原理。

zDF5lV.png
实验结果为将原始图像进行压缩
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 进行重构。

②  计算原图像和压缩后图像的差(相减)

zDFfWq.png
原图像与压缩图像的差反应了失真程度

③ 修改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];
zDF4S0.png
zDF7mF.png
通过以上实验结果对比图片可知,只保留左上角部分系数,图像还原效果并未受大的影响。对于图像来说,其中的边缘、细节上的信号往往是变化比较快,比较剧烈的。所以,图像中的高频信号对应的是图像中的细节和边缘信息。而图像中的大部分内容信息,往往是变换缓慢、频率低的信号。所以,信号中的低频信号对应主要的图像信息。左上角为低频数据,右下角为高频数据,并且数据能量主要集中在低频区域,即低频区域的值比较大,高频区域的值较小。也就是因为高频区域值小,能量低,所以我们可以在接下来的压缩中,将他们置0,在图片质量看起来影响不大的情况下完成压缩。
左上角的1越多保留的细节越多,相反左上角的0越少保留的细节越少,就越模糊


③  使用别的图像进行上述实验,验证结论正确与否

zDFHw4.png
zDFbTJ.png
结论正确

(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();
}

zDFLk9.png
② 彩色的图像分离

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();
}

zDFOYR.png
注意:图像合并时存储图像的顺序需为BGR,刚好与RGB颜色相反,其为imread的默认存储颜色空间顺序,否则合成的图像如下
zDFXf1.png

#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;
}

zDFvSx.png
遇到问题:没有拷贝原图像矩阵直接操作会出错,
解决方案:反转前对原图像矩阵进行深拷贝
zDFxl6.png

赞赏

微信赞赏 支付宝赞赏

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注