2018-04-03 00:22:12 justice0 阅读数 1371
  • 计算机图形学基础

    该课程是计算机图形领域的基础的课程,包含了计算机图形学的数学原理,例如在计算机图形学中的重要坐的标变换的原理。该课程也是游戏开发、VR/AR等开发的基础课程。在该课程中除了讲授数学原理,还包括了C++实践,实现3D数学类。 该课程的主要内容包括:向量、向量空间、矩阵、矩阵空间、仿射空间、齐次坐标系、仿射变换、刚体变换、四元数等。

    2774 人正在学习 去看看 张赐

151220129 南京大学 计科 吴政亿

数字图像处理老师提出了两个思考题
1. 证明几个仿射变换矩阵相乘后还是仿射变换矩阵
2. 证明仿射变换后保持共线性与距离比例不变

第一题

首先,先贴上仿射变换矩阵的定义:

仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,翻转,旋转和剪切。
这类变换可以用一个3*3的矩阵M来表示,其最后一行为(0,0,1)。该变换矩阵将原坐标为(x,y)变换为新坐标(x’,y’),

[uv1]=[abcdef001][xy1]

上式可等价写为矩阵形式,T是满秩矩阵(仿射变换矩阵)
q=Tp

下面证明:
假设一系列仿射变换的矩阵分别为 T1,T2...Tn,那么得到的矩阵q

q=Tn...T2T1p

由于Tn...T2T1=T,其中T的形式也符合仿射矩阵的形式,例如
T1=[a1b1c1d1e1f1001],T2=[a2b2c2d2e2f2001]

则有
T2T1=[a1a2+b1d2a1b2+b1e2a1c2+b1f2+c1d1a2+e1d2d1b2+e1e2d1c2+e1f2+f1001]

仍然符格仿射变换矩阵的形式,递归得Tn...T2T1=T也是仿射变换矩阵,因此任意组合的仿射变换还是仿射变换,即几个仿射变换矩阵相乘后还是仿射变换矩阵

第二题

保持共线性

A,B,C三个点在仿射变换T下新坐标为A,B,C则有

[xaxbxcyaybyc111]=T[xaxbxcyaybyc111]

两边取行列式得:
|xaxbxcyaybyc111|=|T||xaxbxcyaybyc111|

由于T是仿射变换矩阵,故 T 满秩,即 |T|0,又因为
|xaxbxcyaybyc111|=0X,Y,Z线

因此仿射变换后保持共线性

保持距离比例

设点p1,p2,p3共线且p1p2=λ(p2p3),λ0
假设仿射变换为T,只需证:

Tp1Tp2=λ(Tp2Tp3),λ0

下证:
Tp1Tp2=T(p1p2)=λT(p2p3)=λ(Tp2Tp3),λ0

问题得证故仿射变换后保持距离比例
另外,在查阅资料中,我发现这个性质可以再进一步的普适化为:
在仿射变换下, 平行线段的长度比是不变的, 但两个不平行长度的比值不满足。

参考:
1. https://math.stackexchange.com/questions/1289536/prove-that-the-ratio-of-lengths-of-parallel-segments-is-invariant-under-affine-t
2. https://www.cnblogs.com/dupuleng/articles/4055020.html
3. http://web.mnstate.edu/peil/geometry/C3Transform/2model.htm

2019-09-28 10:04:54 qq_43309286 阅读数 12
  • 计算机图形学基础

    该课程是计算机图形领域的基础的课程,包含了计算机图形学的数学原理,例如在计算机图形学中的重要坐的标变换的原理。该课程也是游戏开发、VR/AR等开发的基础课程。在该课程中除了讲授数学原理,还包括了C++实践,实现3D数学类。 该课程的主要内容包括:向量、向量空间、矩阵、矩阵空间、仿射空间、齐次坐标系、仿射变换、刚体变换、四元数等。

    2774 人正在学习 去看看 张赐

数字图像处理之仿射变换

仿射变换,又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。

坐标变换可由:

[ x y 1] = [ν ω 1]T=[ν ω 1]X
[ab0cd0ef1] \left[ \begin{matrix} a & b & 0 \\ c & d & 0 \\ e & f & 1 \end{matrix} \right]

其中(p, q)是原图像中的坐标值,(x, y)是变换图像后的坐标值

在这里插入图片描述
其中仿射变换是对图片的每一个像素点进行操作

2019-04-23 22:24:29 Dujing2019 阅读数 881
  • 计算机图形学基础

    该课程是计算机图形领域的基础的课程,包含了计算机图形学的数学原理,例如在计算机图形学中的重要坐的标变换的原理。该课程也是游戏开发、VR/AR等开发的基础课程。在该课程中除了讲授数学原理,还包括了C++实践,实现3D数学类。 该课程的主要内容包括:向量、向量空间、矩阵、矩阵空间、仿射空间、齐次坐标系、仿射变换、刚体变换、四元数等。

    2774 人正在学习 去看看 张赐

数字图像处理—彩色图像处理

(一) 在 MATLAB 中彩色图像的表示

1.1 RGB图像

一幅 RGB图像就是 M×N×3 大小的彩色像素的数组,其中的每个彩色像素点都是在特定空间位置的彩色图像所对应的红、绿、蓝三个分量。RGB 图像也可以看做由三个灰度图像形成的“堆栈”,当发送到彩色监视器的红、绿、蓝输入端时,就在屏幕上产生彩色图像。 按照惯例,形成一幅 RGB 彩色图像的三幅图像通常被称作红、绿、蓝分量图像

分量图像的数据类决定了它们的取值范围。如果一幅 RGB 图像的数据类是 double,那么取值范围就是 [0,1]。类似的,对于uint8类uint16类的 RGB图像,取值范围分别是[0,255]或[0,65535]。

用来表示这些分量图像像素值的比特数决定了一幅 RGB 图像的比特深度。例如,如果每个分量图像都是 8 比特的图像,那么对应的 RGB图像的深度就是 24 比特。通常,所有分量图像的比特数都是相同的。在这种情况下,一幅 RGB图像可能有的色彩数就是(2b2^b)的三次方,其中的 b 就是每个分量图像的比特数。对于 8 比特图像,颜色数为 16 777 216。

令 fR 、 fG 和 fB 分别表示三幅 RGB 分量图像。RGB 图像就是利用 cat(连接)操作将这些分量图像组合而成的彩色图像:

rab_image = cat(3, fR,  fG , fB) 

在运算中,图像按顺序放置。通常,cat(dim,A1,A2…)沿着由dim指定的方式连接数组 (它们必须是相同尺寸)。例如,如果dim=1,数组就垂直安排;如果dim=2,数组就水平安排; 如果dim=3,数组就按照三维方式堆叠

如果所有的分量图像都是一样的,那么结果是一幅灰度图像

令rgb_image表示一幅RGB图像,下面这些命令可以提取出三个分量图像:

fR = rgb_image(: , : , 1); 
fG = rgb_image(: , : , 2); 
FB = rgb_image(: , : , 3); 

在这里插入图片描述
编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\food2.jpg');
fR = rgb_image(:, :, 1);
fG = rgb_image(:, :, 2);
fB = rgb_image(:, :, 3);
subplot(2, 2, 1), imshow(rgb_image);title('(a)原图像');
subplot(2, 2, 2), imshow(fR);title('(b)红色分量图');
subplot(2, 2, 3), imshow(fG);title('(c)绿色分量图');
subplot(2, 2, 4), imshow(fB) ;title('(d)蓝色分量图');  

代码运行效果如下:

编写代码:


rgb_image=imread('D:\数字图像处理\第六章学习\food2.jpg');
fR = rgb_image(:, :, 1);
fG = rgb_image(:, :, 2);
fB = rgb_image(:, :, 3);
rgb_red=cat(3,fR,zeros(size(fR)),zeros(size(fR)));
rgb_green=cat(3,zeros(size(fR)),fG,zeros(size(fR)));
rgb_blue=cat(3,zeros(size(fR)),zeros(size(fR)),fB);
subplot(2, 2, 1), imshow(rgb_image);title('(a)原图像');
subplot(2, 2, 2), imshow(rgb_red);title('(b)红色分量');
subplot(2, 2, 3), imshow(rgb_green);title('(c)绿色分量');
subplot(2, 2, 4), imshow(rgb_blue) ;title('(d)蓝色分量'); 

代码运行效果如下:

三分量组合成RGB彩色图像

编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\food2.jpg');
fR = rgb_image(:, :, 1);
fG = rgb_image(:, :, 2);
fB = rgb_image(:, :, 3);
rgb_1=cat(3,fR,fB,fB);
rgb_2=cat(3,fR,fR,fG);
rgb_3=cat(3,fB,fG,fG);
subplot(2, 2, 1), imshow(rgb_image);title('(a)原图像');
subplot(2, 2, 2), imshow(rgb_1);title('(b)红绿绿分量组成的彩色图');
subplot(2, 2, 3), imshow(rgb_2);title('(c)红红绿分量组成的彩色图');
subplot(2, 2, 4), imshow(rgb_3);title('(d)绿蓝蓝分量组成的彩色图');  

代码运行效果如下:

RGB 彩色空间常常用 RGB 彩色立方体加以显示,这个立方体的顶点是光的原色(红、绿、蓝)和二次色(青色、紫红色、黄色)。

在这里插入图片描述
为了能从任何透视方向观测这个彩色立方体,可使用自定义函数rgbcube

rgbcube(vx,vy,vz) 

编写代码:

rgbcube(0,0,10);       %3个参数表示观看图像视角的点坐标
%rgbcube(10,10,10);    %3个参数表示观看图像视角的点坐标
axis on;grid on;
title('RGB立方体1');

代码运行效果如下:

实验总结

自然界中任何一种色光都可由R、G、B三基色按不同的比例相加混合而成,当三基色分量都为0(最弱)时混合为黑色光;当三基色分量都为k(最强)时混合为白色光。任一颜色F是这个立方体坐标中的一点,调整三色系数r、g、b中的任一系数都会改变F的坐标值,也即改变了F的色值。可以从RGB图像中提取出红、绿、蓝三个颜色的分量图像,也可由三个分量图像自由组合成彩色图,如果所有的分量图像都是一样的,那么组合结果是一幅灰度图像。从不同透视方向观测RGB彩色立方体,看到的图像是不一样的,RGB彩色空间采用物理三基色表示,因而物理意义很清楚,适合彩色显像管工作。然而这一体制并不适应人的视觉特点。因而,产生了其他不同的颜色空间表示法。

1.2 索引图像

索引图像有两个分量:整数数据矩阵 X 和彩色映射矩阵 map。矩阵 map 是 m×3 大小、由 double 类型且范围在[0,1]之间的浮点数构成的数组。map 的长度 m 等于定义的颜色数。map 的每一行都定义有单色的红、绿、蓝分量。索引图像将像素的亮度值“直接映射”到彩色值。 每个像素的颜色由对应的整数矩阵 X的值作为指向 map的索引决定。如果 X是double类型, 那么值 1 指向 map 的第一行,值 2 指向第二行,等等。如果 X是uint8或uint16类型,那么值 0 指向 map 的第一行。

有时候,用较少的颜色去近似表达索引图像是有必要的。为此,使用函数imapprox, 语法如下:

[Y, newmap] = imapprox (X, map,n) 

这个函数利用彩色映射newmap来返回数组Y,最多有n种颜色。输入数组X的类型可以 是uint8、uint16或double。如果n小于等于256,那么输出Y是uint8类;如果n大于 256,那么Y是double类。

编写代码:

RGB=imread('D:\数字图像处理\第六章学习\flower3.jpg');
[X map]=rgb2ind(RGB,256);
subplot(3, 3, 1),imshow(X,map);           %max(X(:))=255;
title('原始索引彩色图像(256色)');
[Y,newmap]=imapprox(X,map,128);%用较少的128颜色来近似一幅索引图像
subplot(3, 3, 2),imshow(Y,newmap);
title('索引图像(128色)');
[Y,newmap]=imapprox(X,map,64);%用较少的64颜色来近似一幅索引图像
subplot(3, 3, 3),imshow(Y,newmap);
title('索引图像(64色)');
[Y,newmap]=imapprox(X,map,32);%用较少的32颜色来近似一幅索引图像
subplot(3, 3, 4),imshow(Y,newmap);
title('索引图像(32色)');
[Y,newmap]=imapprox(X,map,16);%用较少的16颜色来近似一幅索引图像
subplot(3, 3, 5),imshow(Y,newmap);
title('索引图像(16色)');
[Y,newmap]=imapprox(X,map,8);%用较少的8颜色来近似一幅索引图像
subplot(3, 3, 6),imshow(Y,newmap);
title('索引图像(8色)');
[Y,newmap]=imapprox(X,map,4);%用较少的4颜色来近似一幅索引图像
subplot(3, 3, 7),imshow(Y,newmap);
title('索引图像(4色)');
[Y,newmap]=imapprox(X,map,2);%用较少的2颜色来近似一幅索引图像
subplot(3, 3, 8),imshow(Y,newmap);
title('索引图像(2色)');
[Y,newmap]=imapprox(X,map,0);%用较少的0颜色来近似一幅索引图像
subplot(3, 3, 9),imshow(Y,newmap);
title('索引图像(0色)');

代码运行效果如下:
在这里插入图片描述
在这里插入图片描述
指定彩色映射的办法有很多,一种方法就是利用如下语句:

map(k, :) = [r(k) g(k) b(k)];

其中,[r(k) g(k) b(k)]是RGB值,指定彩色映射的一行。变化的k值可将map填满。

用下面语句中的任何一条都可以把图像的背景色改成想要的颜色:

whitebg('c');   %蓝色
whitebg('y');   %黄色
whitebg('Cyan'); 
whitebg('yellow'); 
whitebg([0 1 1]);  %蓝色
whitebg([1 1 0]); %黄色

代码运行效果如下:


MATLAB提供了一些预定义的彩色映射,可用下面的指令来访问:

colormap(map_name)    %将彩色映射设定为矩阵map_name

编写代码:

colormap([spring;summer;autumn;winter])%相当于自定义了一个256*3维的colormap ,实际上spring,summer,autumn,winter,都是64*3维的colormap
x=[0 1 1 0];
y=[0 0 1 1];                           %定义四个点 [0 0] [1 0] [1 1] [0 1]
fill(x,y,[0 0.1 0.2 0.3]);             %定义四个点的C值 ,则Cmin=0,Cmax=0.3
colorbar;
map=colormap;                          %map为256*3矩阵

代码运行效果如下:

函数 描述
spring 由深红和黄色色调组成
summer 由绿色和黄色色调组成
autumn 从红色到橙色、再到黄色平缓变化
winter 由蓝色和绿色色调组成

实验总结

索引模式和灰度模式比较类似,它的每个像素点也可以有256种颜色容量,但它可以负载彩色。索引模式的图像就像是一块块由彩色的小瓷砖所拼成的,由于它最多只能有256种彩色,所以它所形成的文件相对其它彩色要小得多。索引模式的另一个好处是它所形成的每一个颜色都有其独立的索引标识。当这种图像在网上发布时,只要根据其索引标识将图像重新识别,它的颜色就完全还原了。索引模式主要用于网络上的图片传输和一些对图像像素、大小等有严格要求的地方。

1.3 处理 RGB图像和索引

下表列出了在 RGB图像、索引图像和灰度图像之间进行转换的工具箱函数。

函数 描述
Dither 采用“抖动”方法从 RGB 图像创建索引图像
grayslice 从灰度图像通过阈值处理创建索引图像
gray2ind 从灰度图像创建索引图像
ind2gray 从索引图像创建灰度图像
rgb2ind 从 RGB 图像创建索引图像
ind2rgb 从索引图像创建 RGB 图像
rgb2gray 从 RGB 图像创建灰度图像

1.函数 dither 可以用于灰度图像和彩色图像。将灰度图像转换为二值图像,将彩色图像抖动为索引图像。用于灰度图像的函数dither的语法是:

bw = dither(gray_image) 

rgb_image来表示 RGB图像,用gray_image来表示灰度图像, 用 bw 来表示黑白(二值)图像。

将灰度图像抖动为二值图像

编写代码:

f=rgb2gray(imread('D:\数字图像处理\第六章学习\cat2.jpg'));
subplot(1,2,1);
imshow(f);
title('原图');
bw=dither(f);
subplot(1,2,2);
imshow(bw);
title('将灰度图像抖动为二值图像');

代码运行效果如下:

将彩色图像抖动为索引图像

编写代码:

f=imread('D:\数字图像处理\第六章学习\sun.jpg');
subplot(1,2,1);
imshow(f);
title('原图');
x=dither(f,parula);     %colormap中自带的颜色映射-森莺
subplot(1,2,2);
imshow(x,parula);
title('将彩色图像抖动为索引图像');

代码运行效果:

2.函数grayslice使用阈值对灰度图像进行阈值处理以产生索引图像,有如下语法:

X = grayslice(gray_image, n) 

索引图像可以看做用指令 imshow(X,map)通过长度适当的映射得到的。另一种语法是:

X = grayslice(gray_image, v) 

其中,v 是矢量(值的范围为[0,1]),用来给gray_image 赋阈值。函数 grayslice 是伪彩色图像处理的基本工具,伪彩色为指定的灰度区域赋予不同的颜色。输入图像的类型可以是 uint8、uint16或double。即使输入图像的类型是uint8或uint16,v的阈值也必须在 [0,1]之间。函数执行了必要的缩放操作。

编写代码:

f=rgb2gray(imread('D:\数字图像处理\第五章学习\building.jpg'));
x=grayslice(f,16);
subplot(1,2,1);
imshow(f);
title('灰度图像'); 
subplot(1,2,2);
imshow(x,jet(16));
title('索引图像');

代码运行效果如下:

3.函数gray2ind将灰度图像I转换为索引图像,I 为原灰度图像,n 为灰度等级,默认为64,map中对应的颜色值为颜色图gray(n)中的颜色值。采用如下语法:

[X, map] = gray2ind(I ,n) 

该函数也可将二值图像BW转换为灰度图像,BW只二值图像,n 为灰度等级,默认值为2,二值图像实际上也是灰度图像,只不过灰度级为2。采用如下语法:

[X,map]=gray2ind(BW,n);

编写代码:

I=imread('D:\数字图像处理\第六章学习\face.jpg');
[Y1,map1]=rgb2ind(I,128);                  %提取原始图像的颜色映射表
X=rgb2gray(I);                              %灰度转换
[Y,map]=gray2ind(X,64);                     %灰度图像转化为索引图像
subplot(1,2,1);imshow(X);title('灰度图像');
subplot(1,2,2);imshow(Y,map);title('索引图像');

代码运行效果如下:

4.函数ind2rgb将矩阵 X 和对应的彩色映射map转换成 RGB 格式。X 可以是uint8、uint16 或double类。输出的 RGB图像是大小为 M×N×3 的double类的数组。语法为:

rgb_image = ind2rgb(X, map) 

编写代码:

I=imread('D:\数字图像处理\第六章学习\flower2.jpg');
[Y1,map1]=rgb2ind(I,128);             %提取原始图像的颜色映射表
X=rgb2gray(I);                        %灰度转换
[Y,map]=gray2ind(X,64);               %灰度图像转化为索引图像
subplot(2,2,1);imshow(I);title('原图像');
subplot(2,2,2);imshow(Y,map);title('灰度图像索引图像');
RGB=ind2rgb(Y,map1);                   %还原转化为索引图像以后的RGB
subplot(2,2,3);imshow(RGB);title('转换后的索引转RGB图像');
RGB1=ind2rgb(X,map1);                  %还原转化为索引图像以后的RGB
subplot(2,2,4);imshow(RGB1);title('原灰度矩阵索引转RGB图像');

代码运行效果如下:

(二)彩色空间之间的转换

在一幅 RGB图像中,工具箱直接把颜色描述成 RGB值,或者在索引图像中间接地用 RGB格式来存储彩色映射。 然而,还有其他的彩色空间(又被称作彩色模型),它们在应用中的使用可能比 RGB更方便或更恰当。这些模型是 RGB 模型的变换,包括 NTSC、YCbCr、HSV、CMY、CMYK 和 HSI 彩色空间。工具箱提供了从 RGB 向 NTSC、YCbCr、HSV、CMY 彩色空间转换或转换回来的函数。

2.1 NTSC彩色空间

NTSC 彩色空间用于模拟电视。这种格式的主要优势是灰度信息和彩色数据是分离开来的, 所以同一信号可以用于彩色电视机和黑白电视机。在 NTSC 格式中,图像数据由三部分组成: 亮度(Y)、色调(I)和饱和度(Q)。YIQ分量都是用线性变换从一幅图像的 RGB分量得到的:
在这里插入图片描述
注意: 第一行的各元素之和为 1,而下两行元素的和为 0。这和预想的一样,因为对于一 幅灰度图像,所有的 RGB分量都是相等的;所以对于这样的图像来说,I 和 Q分量应该是 0

获取图像的亮度,色度,饱和度

编写代码:

yiq_image=imread('D:\数字图像处理\第六章学习\flower3.jpg');
fY = yiq_image(:,:,1);
fI = yiq_image(:,:,2);
fQ = yiq_image(:,:,3);
subplot(2, 2, 1), imshow(yiq_image);title('(a)原图像');
subplot(2, 2, 2), imshow(fY);title('(b)亮度');
subplot(2, 2, 3), imshow(fI);title('(c)色度');
subplot(2, 2, 4), imshow(fQ) ;title('(d)饱和度'); 

代码运行效果如下:

函数rgb2ntsc可执行前边的变换:

yiq_image = rgb2ntsc(rgb_image) 

编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\baby.jpg');
yiq_image = rgb2ntsc(rgb_image);
imshow(yiq_image);
subplot(1, 2, 1), imshow(rgb_image);title('(a)原图像');
subplot(1, 2, 2), imshow(yiq_image);title('(b)输出图像');

代码运行效果如下:


其中,输入的 RGB图像可以是uint8、uint16或double类。输出图像是大小为 M×N×3 的 double 类数组。类似的,RGB分量可以利用下面的线性变换从 YIQ分量得到:
在这里插入图片描述
工具箱函数ntsc2rgb执行这个变换,语法是:

rgb_image = ntsc2rgb(yiq_image) 

编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\flower3.jpg');
rgb_image = rgb2ntsc(rgb_image);
f = ntsc2rgb(rgb_image);
imshow(yiq_image);
subplot(1, 2, 1), imshow(rgb_image);title('(a)NTSC空间图像');
subplot(1, 2, 2), imshow(f);title('(b)RGB空间图像');

代码运行情况如下:


输入和输出图像都是double类。

2.2 YCbCr 彩色空间

YCbCr 彩色空间广泛用于数字视频。在这种格式中,亮度信息用单独的分量 Y来表示,彩色信息是用两个色差分量 Cb 和 Cr来存储的。分量 Cb蓝色分量与参考值的差,分量 Cr红色分量与参考值的差。工具箱采用的从 RGB转换为 YcbCr 的变换是:

在这里插入图片描述
转换函数是:

ycbcr_image = rgb2ycbcr(rgb_image)

输入的 RGB 图像可以是 uint8、uint16 或 double类。输出图像和输入图像的类型相同。使用类似的变换可以从 YCbCr 转换回 RGB:

rgb_image = ycbcr2rgb(ycbcr_image) 

编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\Pig2.jpg');
ycbcr_image = rgb2ycbcr(rgb_image)
f = ycbcr2rgb(ycbcr_image) 
subplot(1, 3, 1), imshow(rgb_image);title('(a)输入的RGB图像');
subplot(1, 3, 2), imshow(ycbcr_image);title('(b)RGB空间图像转换为 YCbCr 图像');
subplot(1, 3, 3), imshow(f);title('(c)YCbCr 图像转换为RGB空间图像');

代码运行情况如下:
在这里插入图片描述
在这里插入图片描述
输入的 YCbCr 图像可以是 uint8、uint16 或 double 类。输出图像和输入图像的类型相同。

2.3 HSV彩色空间

HSV(色调、饱和度、值)是人们用来从颜色轮或调色板中挑选颜色(例如颜料或墨水)时使用的彩色系统之一,值得考虑的是,这个颜色系统比 RGB 系统更接近人们的经验和对彩色的感知。HSV颜色空间的模型对应于圆柱坐标系中的一个圆锥形子集,圆锥的顶面对应于V=1。它包含RGB模型中的R=1,G=1,B=1三个面,所代表的颜色较亮。色彩H由绕V轴的旋转角给定。红色对应于角度0°,绿色对应于角度120°,蓝色对应于角度240°。在HSV颜色模型中,每一种颜色和它的补色相差180°。饱和度S取值从0到1,所以圆锥顶面的半径为1。
在这里插入图片描述

从 RGB 到HSV 的转换

数学公式推算

设 (r, g, b) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 r, g 和 b 中的最大者。设 min 等于这些值中的最小者。要找到在 HSV 空间中的 (h, s, v) 值,这里的 h ∈ [0, 360)是角度的色相角,而 s, v ∈ [0,1] 是饱和度和亮度,计算为:

max=max(R,G,B)
min=min(R,G,B)
if R = max, H = (G-B)/(max-min)
if G = max, H = 2 + (B-R)/(max-min)
if B = max, H = 4 + (R-G)/(max-min)

H = H * 60
if H < 0, H = H + 360

S=(max-min)/max
V=max(R,G,B)

注意:h 的值通常规范化到位于 0 到 360°之间。而 h = 0 用于 max = min 的(就是灰色)时候而不是留下 h 未定义。

从 HSV 到 RGB 的转换

数学公式推算

类似的,给定在 HSV 中 (h, s, v) 值定义的一个颜色,带有如上的 h,和分别表示饱和度和明度的 s 和 v 变化于 0 到 1 之间,在 RGB 空间中对应的 (r, g, b) 三原色可以计算为:
在这里插入图片描述在这里插入图片描述
将 RGB转换为 HSV的 MATLAB函数是rgb2hsv,语法如下:

hsv_image = rgb2hsv(rgb_image) 

输入的 RGB图像可以是uint8、uint16或double类,输出图像是double类。将 HSV 转换回 RGB的函数为hsv2rgb,语法如下:

rgb_image = hsv2rgb (hsv_image) 

编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\flower5.jpg');
hsv_image = rgb2hsv(rgb_image)
f = hsv2rgb(hsv_image) 
subplot(1, 3, 1), imshow(rgb_image);title('(a)输入的RGB图像');
subplot(1, 3, 2), imshow(hsv_image);title('(b)RGB空间图像转换为HSV图像');
subplot(1, 3, 3), imshow(f);title('(c)HSV图像转换为RGB空间图像');

代码运行效果如下:

在这里插入图片描述在这里插入图片描述
上面图片只是简单的转换,看不出来HSV图像的优势,下面进行图像颜色调整

编写代码

h = input('Adjust hue by: ');        % 输入色度调整参数h
s = input('Adjust saturation by: ');        % 输入饱和度调整参数s
v = input('Adjust intensity by: ');        % 输入明度调整参数v

I = imread('D:\数字图像处理\第六章学习\flower4.jpg');    % 读取源图像并显示
figure(1);
imshow(I);title('(a)原图像');
% HSV颜色空间处理
hsv_image = rgb2hsv(I);        % 将rgb颜色空间转换为hsv颜色空间
hue = hsv_image(:,:,1);        % 提取色度通道
sat = hsv_image(:,:,2);        % 提取饱和度通道
int = hsv_image(:,:,3);        % 提取明度通道

hue = hue + 1 / 360 * h;    % 对各通道进行处理
sat = sat + 0.01 * s;
int = int + 0.01 * v;

%hue(hue>1)=hue(hue>1) - 1; hue(hue<0)=1 - hue(hue<0);
hue(hue>1)=1; hue(hue<0)=0;
sat(sat>1)=1; sat(sat<0)=0;
int(int>1)=1; int(int<0)=0;

hsv_image(:,:,1) = hue;        % 运算后的各通道返回原图
hsv_image(:,:,2) = sat;
hsv_image(:,:,3) = int;

% matlab 无法直接显示hsv和hsi图像,还要先转换为rgb
rgb = hsv2rgb(hsv_image);    % 将hsv颜色空间转换为rgb颜色空间,并显示
figure(2);
imshow(rgb);title('(b)HSV颜色空间处理后的图像');

代码运行效果如下:

实验总结

HSV(Hue Saturation Value)颜色模型是面向用户的。在图像处理中,通常情况下不会直接对RGB图像做处理,这主要是因为RGB与人类的视觉感知相差较远,而HSV就是常用的色彩空间。比如,某些由于光照不同造成的视觉感知不同的场景下,通常用Value这个通道进行计算,通过计算梯度,可以较好的提取物体边缘。再比如某些场景下,前景饱和度较高,背景采用饱和度较低的颜色来衬托前景,这时Saturation这个通道的信息就非常有用了。再比如,在某些室内场景下,风格较为单一,一般一个物体只有一种颜色,这时候Hue这个通道就显得尤为重要了。总之,HSV色彩空间在图像处理领域应用非常广泛,一般通过采用合适的通道就能完成大多数图像预处理工作。从上图(西湖荷花)也可以看出进行了色度,明度,饱和度的调整,图像变得更加鲜艳,接近物体本来的色彩,更符合人的视觉。

2.4 CMY和 CMYK

CMY(青,深红,黄)通过颜色相减来产生其他颜色的。大多数将颜料堆积于纸上的设备,比如彩色打印机和复印机,都需要 CMY 数据输入,或在内部将 RGB转换为 CMY,近似的转换可用下面的公式实现:
在这里插入图片描述
其中,假想所有的颜色值都已经归一化在[0,1]之间。这个公式证明从涂满纯青色的表面反射的光不包含红色(公式中的 C = 1–R )。同样,纯净的紫红色不反射绿色纯净的黄色不反射蓝色。上述公式还证明,从 1 减去个别的 CMY 值,可以从一组 CMY 值很容易地获得 RGB值。

imcomplement函数可近似地把 RGB模型转换为 CMY模型:

cmy_image = imcompliment(rgb_image) 

也可以用这个函数将 CMY图像转换为 RGB图像:

rgb_image = imcompliment(cmy_image) 
rgb_image=imread('D:\数字图像处理\第六章学习\flower5.jpg');
hsi_image = imcomplement(rgb_image);
j=imcomplement(hsi_image);
subplot(1, 3, 1), imshow(rgb_image);title('(a)输入的RGB图像');
subplot(1, 3, 2), imshow(hsi_image);title('(b)RGB空间图像转换为CMY图像');
subplot(1, 3, 3), imshow(j);title('(c)CMY图像转换为RGB空间图像');

在这里插入图片描述
在理论上,等量的颜料原色,将青色、紫红色和黄色混合会产生黑色。在实践中,将这些颜色混合印刷会生成模糊不清的黑色。所以,为了生成纯正的黑色(打印中主要的颜色),第 4 种颜色——黑色便添加进来了,从而给出提升的 CMYK彩色模型。这种模式的文件大,占用的磁盘和内存大,一般在印刷时使用。

2.5 HSI彩色空间

HSI(色彩、饱和度、强度),该模型将强度分量与从一幅彩色图像中承载的彩色信息分开。正如结果那样,HSI 模型是开发基于彩色描述的图像处理算法的理想工具

1.将颜色从 RGB 转换为 HSI

每个 RGB像素的 H分量可用下面的公式得到:
在这里插入图片描述
其中:
在这里插入图片描述
饱和度由下面的式子给出:
在这里插入图片描述
最后,亮度由下面的式子给出:
在这里插入图片描述
将从 H 的公式中得出的所有结果除以 360°,即可将色调归一化在[0,1]之间。 如果给出的 RGB值在[0,1]之间,那么其他的两个 HSI 分量就已经在[0,1]之间了。

下面是惯用的函数:

hsi = rgb2hsi(rgb) 

编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\sun.jpg');
hsi_image = rgb2hsi(rgb_image)
subplot(1, 2, 1), imshow(rgb_image);title('(a)输入的RGB图像');
subplot(1, 2, 2), imshow(hsi_image);title('(b)RGB空间图像转换为HIS图像');

代码运行效果如下:

2.将颜色从HSI 转换为RGB

有三个感兴趣的部分,正如早些时候提到的那样,分别对应原色之间相隔 120° 的范围。用 360°乘以 H,这样就将色调的值还原成了原来的范围——[000^0,36000^0]。

RG区域(000^0≤H≤36000^0)如果 H在这个区域内,那么 RGB分量由下式给出:

在这里插入图片描述

在这里插入图片描述
GB区域 (12000^0≤H<24000^0)如果给出的H值在这个区域内,我们就先从中减去120°:
在这里插入图片描述
那么,这时 RGB分量是:
在这里插入图片描述
并且
在这里插入图片描述
BR 区域(240°≤H≤360°) 最后,如果 H在这个区域内,我们就从中减去 240°:
在这里插入图片描述
RGB分量分别是:
在这里插入图片描述
其中:
在这里插入图片描述

在这里插入图片描述
编写代码:

rgb_image=imread('D:\数字图像处理\第六章学习\building.jpg');
hsi_image = rgb2hsi(rgb_image);
j = hsi2rgb(hsi_image);
subplot(1, 3, 1), imshow(rgb_image);title('(a)输入的RGB图像');
subplot(1, 3, 2), imshow(hsi_image);title('(b)RGB空间图像转换为HIS图像');
subplot(1, 3, 3), imshow(j);title('(c)HIS图像转换为RGB空间图像');

代码运行效果如下:
在这里插入图片描述
在这里插入图片描述
上面图片只是简单转换,看不出来HSI图像的优势,下面进行图像颜色调整:

编写代码:

h = input('Adjust hue by: ');        % 输入色度调整参数h
s = input('Adjust saturation by: ');        % 输入饱和度调整参数s
i = input('Adjust intensity by: ');        % 输入强度调整参数i

I = imread('D:\数字图像处理\第六章学习\flower4.jpg');    % 读取源图像并显示
figure(1);
imshow(I);title('(a)原图像');
% HSI颜色空间处理
hsi_image = rgb2hsi(I);        % 将rgb颜色空间转换为hsi颜色空间
hue = hsi_image(:,:,1);        % 提取色度通道
sat = hsi_image(:,:,2);        % 提取饱和度通道
int = hsi_image(:,:,3);        % 提取明度通道

hue = hue + 1 / 360 * h;       % 对各通道进行处理
sat = sat + 0.01 * s;
int = int + 0.01 * i;

%hue(hue>1)=hue(hue>1) - 1; hue(hue<0)=1 - hue(hue<0);
hue(hue>1)=1; hue(hue<0)=0;
sat(sat>1)=1; sat(sat<0)=0;
int(int>1)=1; int(int<0)=0;

hsi_image(:,:,1) = hue;        % 运算后的各通道返回原图
hsi_image(:,:,2) = sat;
hsi_image(:,:,3) = int;

rgb = hsi2rgb(hsi_image);    % 将hsi颜色空间转换为rgb颜色空间,并显示
figure(3);
imshow(rgb);title('(b)hsi颜色空间转换后的图像');

代码运行效果如下:

实验总结

由图可以明显看出HSI模型相对于RGB模型更适宜用于彩色图像的增强,完全反映了人感知颜色的基本属性,与人感知颜色的结果一一对应,因此,HSI模型被广泛应用于人的视觉系统感知演的的图像表示和处理系统中。并且在处理彩色图像时,可仅对I分量进行处理,结果不改变原图像中的彩色种类,所以HSI 模型是开发基于彩色描述的图像处理算法的理想工具

2.6 独立于设备的彩色空间

彩色空间以使计算更方便的方法描述了彩色信息,或以对特殊应用更直观或更合适的方法描述彩色。迄今为止讨论的所有空间都与设备相关。下面集中讨论独立于设备的彩色空间。

2.6.1 背景

通常,在辨别不同颜色时使用的特征是亮度、色调和饱和度。亮度具体表达无色的强度的概念,色调是在光波混合中与主要波长相关的属性,色调描述了观察者感知的主要彩色,饱和度指的是相对纯度或与色调相混合的白光数量。

色调和饱和度合起来称为颜色,因此,彩色是由颜色和亮度表现的特性。为形成一种特殊颜色而需要的红、绿、蓝颜色叫做三色值,分别用 X、Y、Z 来表示。颜色是由颜色自身的三色系数来指定的,定义为:
在这里插入图片描述
并且:
在这里插入图片描述
其中,x、y 和 z 分别表示红、绿、蓝分量。在可见光谱中,光的任何波长均可产生与那个波长相对应的三色值。以 x 和 y 为参数的函数显示人能感觉到的颜色范围的图被称为色度图。在色度图中,连接任意两个点的直线段说明了所有不同的颜色变化,可以由相加的两种颜色混合得到。由任何三个固定颜色处的顶点形成的三角形不能包括图中的全部颜色区域,所以任何颜色都可从确定的三原色产生是误解

2.6.2 sRGB 彩色空间

RGB彩色模型依赖于设备。这意味着对于给定的一组 R、 G、B 值,不存在单一、明确的彩色解释。另外,图像文件常常不包含获取图像时所用设备的彩色特性信息。就像结果那样,相同的图像文件可能(经常就是)在不同的计算机系统中看上去明显不同。

解决这个问题,微软和惠普提出了新的默认彩色空间标准,称作 sRGB。sRGB 彩色空间被设计为与计算机的CRT监视器标准特性相一致,并且与PC机在家庭和办公室观察环境相一致。 sRGB彩色空间独立于设备。因此,sRGB颜色值很容易被改变为另一个独立于设备的彩色空间。

sRGB 标准已变成计算机界广泛接受的标准,特别是面向消费者的设备。数字摄像机、扫 描仪、计算机显示器和打印机等,都例行地被设计为假定图像的 RGB值与 sRGB的彩色空间是一致的,除非图像文件包含更多的指定设备的彩色信息。

2.6.3 CIE 和 sRGB 彩色空间之间的转换

工具箱函数 makecform 和 applycform 可用于独立于设备的彩色空间之间的转换。

以Lab彩色空间为基础创建在感觉上的一致彩色空间

构建一个彩色标尺,可用于彩色和黑色出版物,设计在40和80之间的间隔相等的估值为1024的斜坡;

编写代码:

L=linspace(40,80,1024);
radius=70;
theta=linspace(0,pi,1024);
a=radius*cos(theta);
b=radius*sin(theta);
%我们制作一幅 L*a*b* 彩色标尺的 100×104×3 大小的图像: 
L=repmat(L,100,1);  
a=repmat(a,100,1);
b=repmat(b,100,1);
lab_scale=cat(3,L,a,b);
cform =makecform('lab2srgb');
rgb_scale= applycform(lab_scale,cform);
imshow(rgb_scale);

代码运行效果如下:
在这里插入图片描述
图为 基于 Lab*彩色空间的在感觉上一致的标尺 。

2.6.4 ICC彩色剖面

每一个设备正好有两个与之关联的变换,而不管在系统中可能存在的其他设备的数量。其中的一个变换把设备彩色转换为标准,独立于设备的彩色空间叫做剖面连接空间(Profile Connection Space, PCS)。另一个变换是第一个变换的反变换,用来把 PCS彩色转换回设备彩色(PCS不是 XYZ, 就是 Lab*)。这两个变换合起来构成设备的 ICC 彩色剖面

ICC 的主要目的之一是创建标准化的、维护和提升 ICC彩色剖面的标准。图像处理工具箱 函数iccread读取剖面文件,语法是:

p = iccread(filename) 

输出p是包含文件头信息和数字参数,以及计算设备和PCS间彩色空间转换所必需的表格。 使用 ICC 剖面转换彩色是通过使用makecform和applycform实现的。 makecform的语法是:

cform = makecform('icc', src_profile, dest_profile) 

其中,src_profile是源设备剖面的文件名,dest_profile是目的设备剖面的文件名。

ICC 彩色剖面的软实验

编写代码:

f=imread('D:\数字图像处理\第六章学习\girl.png');
fp = padarray(f, [40 40], 255, 'both'); 
fp = padarray(fp, [4 4], 230, 'both'); 
p_srgb = iccread('E:\Code\sRGB.icm');           %描述sRGB彩色空间的剖面
p_snap = iccread('E:\Code\SNAP2007.icc');       %新闻纸剖面
cform1 = makecform('icc', p_srgb, p_snap); 
fp_newsprint = applycform(fp, cform1); 
cform2 = makecform('icc', p_snap, p_srgb,  'SourceRenderingIntent', 'AbsoluteColorimetric', 'DestRenderingIntent', 'AbsoluteColorimetric'); 
fp_proof = applycform(fp_newsprint, cform2);   %指定渲染意图
subplot(1, 2, 1), imshow(fp);title('(a)具有白边的原始图像');
subplot(1, 2, 2), imshow(fp_proof);title('(b)当在新闻纸上打印时对图像外观的模拟 ');

代码运行效果如下:

实验总结

实验中首先用环绕图像的拥有粗白边和细灰边的边界对图像做预处理。这些边界将使得更容易观察对新闻用纸“白”度的模拟,图(a)显示了填充的图像,然后读进两个剖面并用它们把虹膜图像从 sRGB转换为新闻纸颜色,最后用绝对色度的渲染意图创建cform结构,以便为了显示而将之转换回 sRGB,图 (b)显示了结果。这幅图像本身仅是在计算机监视器上看到的实际结果的近似,因为印刷书籍的彩色全域与监视器的彩色全域不同。

(三)彩色图像处理的基础知识

彩色图像处理细分成 3 个主要领域:

  1. 颜色变换(也叫彩色映射)
  2. 单独彩色平面的空间处理
  3. 颜色向量的处理

第 1 类处理每个彩色平面的像素,这类处理严格地以像素值为基础,而不是它们的空间坐标,类似于灰度变换处理。第 2 类处理涉及各个彩色平面的空间(邻域)滤波, 类似于空间滤波。第 3 类处理涉及以同时处理彩色图像的所有分量为基础的处理技术。因为全彩图像至少有三个分量,彩色像素可以用向量来处理。

令 c 代表 RGB彩色空间中的任意向量:
在这里插入图片描述
这个公式指出,c 分量是一副彩色图像在某个点上的 RGB分量。考虑彩色分量是坐标的函数这样的事实,用下边的符号表示:
在这里插入图片描述
对于一幅大小为 M×N 的图像,有 MN 个这样的向量 c(x,y),其中,x=0,1,2…,M–1 且 y=0,1,2…,N–1。

在某些情况下,无论彩色图像每次处理一个平面,还是作为向量处理,都会得到相等的结果。然而,不会总是这样的情况。为了使两种方法都相同, 两个条件必须满足:首先,处理必须对向量和标量都可用。其次,针对向量的每个分量的操作必须与其他分量无关

(四)彩色变换

这里描述的是单一彩色模型情况下的技术,以处理彩色图像的彩色分量或单色图像的亮度分量为基础。对于彩色图像,限定如下形式的变换:

si = Ti(ri) i = 1,2,…,n

这里,ri 和 si 是输入和输出图像的彩色分量,n 是 ri是彩色空间的维数(或是彩色分量的数量),并且 Ti 是全彩色变换(或叫映射)函数。如果输入图像是单色的,公式将如下所示:

si = Ti( r ) i = 1,2,…,n

r表示灰度级的值,n 是在 Si中彩色分量的数量。这个公式描述了灰度级对任意颜色的映射,这一处理在伪彩色变换或伪彩色映射中经常提到。 注意,如果我们让 r1=r2=r3=r,第一个公式可用来处理 RGB中的单色图像。

三次样条内插用spline函数来实现:

Z = spline(x,y,xi) 

编写代码:

x=0:10;
y=sin(x);
subplot(121),plot(x,y,'+',x,y,'r');%画xy图像,并标出x和y点
xx=0:.25:10;
yy=spline(x,y,xx);
subplot(122),plot(x,y,'o',xx,yy,'b');%也是画xy图像,标出x和y点,但是这中间插值了隔.25的数,只是没被标出来

代码运行效果如下:

变换函数的说明可以用图形法操作控制点的方式交互地产生,那些控制点输入到 interpiq和spline函数并实时地显示将被处理的图像的结果。语法是:

g = ice('property name', 'property value',…) 

最后的结果是带有句柄的图像g(通常是图形目标)。

编写代码:

f=imread('D:\数字图像处理\第六章学习\cat5.jpg');
g=ice('image',f);

代码运行效果如下:

编写代码:

f=imread('D:\数字图像处理\第六章学习\cat5.jpg');
g=ice('image',f,'space','CMY');%修改CMY彩色空间

代码运行效果如下:

编写代码:

f=imread('D:\数字图像处理\第六章学习\cat5.jpg');
g=ice('image',f,'space','hsi');

代码运行效果如下:

‘space’是被修改的分量彩色空间。可能的值是’rgb’、‘cmy’、‘hsi’、‘hsv’、‘ntsc’(或’yiq’)和’ycbcr’,默认值是’rgb’ 。

2019-04-21 20:27:44 Dujing2019 阅读数 542
  • 计算机图形学基础

    该课程是计算机图形领域的基础的课程,包含了计算机图形学的数学原理,例如在计算机图形学中的重要坐的标变换的原理。该课程也是游戏开发、VR/AR等开发的基础课程。在该课程中除了讲授数学原理,还包括了C++实践,实现3D数学类。 该课程的主要内容包括:向量、向量空间、矩阵、矩阵空间、仿射空间、齐次坐标系、仿射变换、刚体变换、四元数等。

    2774 人正在学习 去看看 张赐

数字图像处理—几何变换与图像配准

几何变换改变了图像中像素间的空间关系。可以使图像放大和缩小,可以旋转、移动或用各种方法进行其他扩展。几何变换可用于创建小场景,使之适应从某个重放分辨率到另一个分辨率的数字视频,校正由观察几何变化导致的失真,以及排列有相同场景和目标的多幅图像。

(一)点变换

假设(w,z)和(x,y)是两个空间坐标系统,分别称为输入空间和输出空间。几何坐标变换可定义为输入空间点到输出空间点的映射:
在这里插入图片描述
这里, T{.}叫做正向变换或正向映射。如果 T{.}有逆,可逆映射输出空间点到输入空间点:
在这里插入图片描述
图像的几何变换被定义为按照几何坐标的变换。令f(w,z)表示输入空间的一幅图像,我们可以定义输出空间的变换图像g(x,y)。

图像处理工具箱采用tform 结构来描述几何坐标变换,tform 结构由函数 maketform 创建,语法是:

tform = maketform(transform—type, params, ...)

参量 transform_type 是下列字符串之一:’affine’、’projective’、’custom’、 ‘box’或’composite’。主要讨论‘custom’变换类型,这种变换可以用来创建基于用户定义的几何坐标变换的 tform 结构。'custom’类型的全部语法是:

tform = maketform('custom', ndims_in, ndims_out,... forward_fen, inv_function, tdata)

对于二维几何变换是 ndims_in 和 ndims_out,参数 forward_fen 和 inv_fen 是关于正向和反向空间坐标变换的函数句柄 。参数 tdata 包含 forward_fcn 和所需要的任何额外信息。

接下来实际操作,创建两个描述不同空间坐标变换的 tform 结构。第一个变换用因数3水平地放大,用因数2垂直地放大:
在这里插入图片描述在这里插入图片描述
首先,创建正向函数,语法:

xy = fwd_function (wz,tdata) 

wz是包含两列的矩阵,在 wz 平面中每一行包含一个点;xy是另一个包含两列的矩阵,其中的行是包含在 xy 平面中的点。

 forward_fcn=@(wz,tdata) [3*wz(:,1),2*wz(:,2)]

在这里插入图片描述
接下来,创建具有语法wz = inverse_fen(xy,tdata)的反函数:

inverse_fcn=@(xy,tdata) [xy(:,1)/3,xy(:,2)/2]

在这里插入图片描述
现在,可以构建第一个 tform 结构:

tform1=maketform('custom',2,2,forward_fcn,inverse_fcn,[])

在这里插入图片描述
计算某个点对的正向变换,接下来的反向变换核实了我们得到的原始数据:

WZ=[1 1;3 2];
XY=tformfwd(WZ,tform1)

在这里插入图片描述

WZ2=tforminv(XY,tform1)

在这里插入图片描述

第 2 个变换示例:以垂直坐标因子移动水平坐标,并保持垂直坐标不变。
在这里插入图片描述
在这里插入图片描述
编写代码:

forward_fcn=@(wz,tdata) [wz(:,1)+0.4*wz(:,2),wz(:,2)];
inverse_fcn=@(xy,tdata) [xy(:,1)-0.4*xy(:,2),xy(:,2)];
tform2=maketform('custom',2,2,forward_fcn,inverse_fcn,[]);
XY = tformfwd{WZ, tform2)

在这里插入图片描述

WZ2=tforminv(XY,tform2)

在这里插入图片描述
对应垂直坐标的 xy的第二列在变换中没有变。

为了能对特殊的空间变换的效果有一个较好的感觉,目测变换对在网格上安排一组点的影响是有帮助的。下面的两个自定义 M-函数 pointgrid 和 vistform 可帮助目测检验给定的变换。

vistform(tforml, pointgrid([0 0;100 100])) 
figure, vistform(tform2, pointgrid([0 0;100 100]))

运行效果如下:

如图所示,第一个变换以不同的缩放因子延伸了水平和垂直轴;第二个变换以随垂直坐标变化的数量水平移动点。

(二)仿射变换

对于 2D空间,仿射变换可写成下式:
在这里插入图片描述
为了数学和计算方便,仿射变换可用附加第三坐标的方法写成矩阵相乘形式:
在这里插入图片描述
这个公式还可以写成下式:
在这里插入图片描述
其中,T称为仿射矩阵。在[x y]和[w z]向量上加 1 的符号约定将产生齐次坐标。对应tforml 的仿射矩阵是:
在这里插入图片描述
对应tforml 的仿射矩阵是:
在这里插入图片描述
函数 maketform 语法 tform = maketform(‘affine’, T) 从仿射矩阵直接创建 tform 结构。
编写代码:

T = [1 0 0;0.4 1 0; 0 0 1];
tform3 = maketform('affine', T);
WZ = [1 1; 3 2];
XY = tformfwd(WZ, tform3);

在这里插入图片描述
在这里插入图片描述
重要的仿射变换包括缩放、旋转、平移、裁剪和反射。下面分别介绍这几类:

图像缩放

图像比例缩放是指将给定的图像在x轴方向按比例缩放xf倍,在y轴按比例缩放yf倍,从而获得一幅新的图像。如果fx=fy,即在x轴方向和y轴方向缩放的比率相同,称这样的比例缩放为图像的全比例缩放。如果fx不等于fy,图像的比例缩放会改变原始图象的像素间的相对位置,产生几何畸变。

编写代码:

img1=imread('D:\数字图像处理\第五章学习\dog2.jpg');
se=translate(strel(1),[100 100]);
img2=imresize(img1,0.2,'nearest');
img3=imresize(img1,1.5,'nearest');
figure(1);
imshow(img1),title('(a)原图像');
figure(2);
imshow(img2),title('(b)缩小图');
figure(3);
imshow(img3),title('(c)放大图');

代码运行效果如下:

图像平移

图像平移就是将图像中所有的点都按照指定的平移量水平、垂直移动。

数学理论原理
设图像的高度为H,宽度为W,如下所示:
在这里插入图片描述
图像是由像素组成的,而像素的集合就相当于一个二维的矩阵,每一个像素都有一个“位置”,也就是像素都有一个坐标。假设原来的像素的位置坐标为(x0,y0),经过平移量(△x,△y)后,坐标变为(x1,y1),如下所示:
在这里插入图片描述
用数学式子表示可以表示为:

x1 = x0 + △x,
y1 = y0 + △y;

用矩阵表示为:
在这里插入图片描述
编写代码:

img1=imread('D:\数字图像处理\第五章学习\cat3.jpg');
se=translate(strel(1),[100 100]);
img2=imdilate(img1,translate(strel(1),[-100 -100]));
img3=imdilate(img1,translate(strel(1),[100 -100]));
img4=imdilate(img1,translate(strel(1),[100 100]));
subplot(2, 2, 1),imshow(img1);title('(a)原图像');
subplot(2, 2, 2),imshow(img2);title('(b)左上方平移');
subplot(2, 2, 3),imshow(img3);title('(c)左下方平移');  
subplot(2, 2, 4),imshow(img4);title('(c)右下方平移');  

代码运行效果如下:
在这里插入图片描述
图片镜像变换

图像的镜像变换分为两种:一种是水平镜像,另外一种是垂直镜像。

数学理论原理

图像的水平镜像操作是将图像的左半部分和右半部分以图像垂直中轴线为中心镜像进行对换,示意图如下:

水平镜像中,原图中的(x0,y0)经过水平镜像后,坐标变成了(x0,W-y0),用数学公式表达就是:

x1 = x0;
y1 = W-y0 ;

写成矩阵就是:
在这里插入图片描述
也就是说,水平镜像变幻的矩阵因子为:
在这里插入图片描述
图像的垂直镜像操作是将图像上半部分和下半部分以图像水平中轴线为中心镜像进行对换。示意图如下:

垂直镜像中,原图中的(x0,y0)经过垂直镜像后,坐标变成了(H-x0,y0),用数学公式表达就是:

x1 = H-x0;
y1 = y0 ;

写成矩阵就是:
在这里插入图片描述
也就是说,垂直镜像变换的矩阵因子为:
在这里插入图片描述
编写代码:

img1=imread('D:\数字图像处理\第五章学习\dog6.tif');
tform1=maketform('affine',[-1 0 0;0 1 0;1 0 1]);
img2=imtransform(img1,tform1,'nearest');
tform2=maketform('affine',[1 0 0;0 -1 0;0 1 1]);
img3=imtransform(img1,tform2,'nearest');
subplot(1, 3, 1),imshow(img1);title('(a)原图像');
subplot(1, 3, 2),imshow(img2);title('(b)水平镜像');
subplot(1, 3, 3),imshow(img3);title('(c)垂直镜像');  

代码运行效果如下:
在这里插入图片描述
图像旋转
一般图像的旋转是以图像的中心为原点,旋转一定的角度。

数学理论原理

图像像素原来的坐标为(x0,y0),(顺时针)选择Θ角度后得到(x1,y1),用数学公式表达如下所示:
在这里插入图片描述
在这里插入图片描述
用矩阵表示如下所示:
在这里插入图片描述
编写代码:

img1=imread('D:\数字图像处理\第五章学习\cat2.jpg');
img2=imrotate(img1,270);
img3=imrotate(img1,90);
img4=imrotate(img1,180);
subplot(2, 2, 1),imshow(img1);title('(a)原图像');
subplot(2, 2, 2),imshow(img2);title('(b)旋转270度');
subplot(2, 2, 3),imshow(img3);title('(c)旋转90度');  
subplot(2, 2, 4),imshow(img4);title('(d)旋转180度');  

代码运行效果如下:
在这里插入图片描述
注意:这里主要是说明一下imrotate函数,这个函数就是对图像旋转的函数,输入是图像和旋转的角度,角度为正值时,逆时针旋转;角度为负值时,顺时针选择。

图像的裁剪

有时候我们为了减少图像所占存储空间,舍弃图像的无用部分,只保留感兴趣的部分,则需要用到图像的剪取。

编写代码:

img1=imread('D:\数字图像处理\第五章学习\face.jpg');
img2=imcrop(img1,[100 100 100 100]);  %从坐标(0,0)开始,从原图上剪取一个长度为50,宽度为50的矩形部分
img3=imcrop(img1,[170 200 80 70]);
img4=imcrop(img1,[160 273 100 70]);
subplot(2, 2, 1),imshow(img1);title('(a)原图像');
subplot(2, 2, 2),imshow(img2);title('(b)剪取眼睛部分');
subplot(2, 2, 3),imshow(img3);title('(c)剪取鼻子部分');  
subplot(2, 2, 4),imshow(img4);title('(d)剪取嘴巴部分');  

代码运行效果如下:
在这里插入图片描述
实验结论:图像的放大,缩小,旋转,裁剪等图像几个变换的理论和实验过程已经广泛应用在许多领域,在图像处理中的许多方法和技术,包括图像增强,图像恢复,压缩编码等都以此为基础。但同时MATLAB进行几何变换也存在一些技术局限,如当图像面积太大 ,利用MATLAB实现图像的放大效果不明显,而且运用MATLAB进行裁剪时,要注意裁剪范围坐标是否超出原图像范围。

(三)投影变换

另一种有用的几何变换类型是投影变换,包括在空间情况下的仿射变换的投影变换在一幅 图像的逆透视变换中是很有用的。正如仿射变换那样,这对使用辅助第三维定义二维投影变换很有用。然而,与仿射变换不同,辅助坐标(在下边的公式中用 h来表示)不是常量。
在这里插入图片描述
其中,a11和a23是非零值, 在投影变换中,线映射为线,但是平行线不再保持平行。为了创建投影的 tform 结构,采用带有 maketform 函数的’projective’ 类型,例如:

编写代码:

T = [-2.7390 0.2929 -0.6373 
    0.7426 -0.7500 0.8088 
    2.8750 0.7500 1.0000];
tform = maketform('projective', T);
vistform(tform, pointgrid([0 0; 1 1]));

代码运行效果如下:

左边图为输入空间的点网格,右图为输出空间中变换后的点网格。 输入空间网格中有两组平行线, 一组是水平的,一组是垂直的。
在这里插入图片描述
上图显示了这两组平行线变换到输出空间后的线,在称为尽头点的位置交叉。尽头点位于地平线上,当变换的时候,仅平行于水平线的输入空间的线保持水平,所有的其他平行线变换为在位于地平线上尽头点处交叉的线。

(四)应用于图像的几何变换

图像处理工具箱函数 imtransform 使用逆映射过程把几何变换应用于图像。imtransform 的基本调用语法是:

g = imtransform(f, tform)
T=maketform(transformtype,Matrix);

编写代码:

f = checkerboard(50);
sx = 0.75;
sy = 1.25;
T = [sx 0 0
    0 sy 0
    0 0 1];
t1 = maketform('affine',T);     %创建缩放参数结构体
g1 = imtransform(f, t1);        %实现图像的缩放
theta = pi/6;

T2 = [ cos(theta) sin(theta) 0 
    -sin(theta) cos(theta) 0 
     0 0 1];
 t2 = maketform('affine', T2);    %创建旋转参数结构体
 g2 = imtransform(f, t2);         %实现图像旋转
 
 T3 = [0.4788 0.0135 -0.0009 
     0.0135 0.4788 -0.0009 
     0.5059 0.5059 1.0000]
 tform3 = maketform('projective',T3);
 g3 = imtransform(f, tform3);
 
subplot(2, 2, 1),imshow(f);title('(a)原图像');
subplot(2, 2, 2),imshow(g1);title('(b)仿射缩放变换');
subplot(2, 2, 3),imshow(g2);title('(c)仿射旋转变换');  
subplot(2, 2, 4),imshow(g3);title('(d)投影变换');  

代码运行效果如下:

总的来说,仿射变换可以理解为经过对坐标轴的放缩,旋转,平移后原坐标在在新坐标域中的值。更简洁的说:仿射变换=线性变换+平移。参数transformtype指定了变换的类型,如上面的‘affine’为二维仿射变换,包括平移、旋转、比例、拉伸、错切等;Matrix为相应的仿射变换矩阵。

(五)MATLAB 中的图像坐标系统

像前面显示的许多图没有轴标记,这是函数imshow的默认行为。但是几何图像变换的分析和解释是通过显示这些形象的排列来辅助的。开启标记的一种方法是在调用imshow之后调用axis。例如:

f=imread('D:\数字图像处理\第五章学习\face.jpg');
imshow(f)
axis on
xlabel x
ylabel y

代码运行效果如下:

原点在左上角; X 轴是水平的,并向右延伸; 轴是垂直的, 并向下延伸。用于设置一定的用户参数选择的工具箱函数 iptsetpref 可用于始终产生 imshow 以显示记号标志。为转向带有记号和标记的显示,可调用:

iptsetpref imshowAxesVisible on

XData特性是两元素向量,其中,第一个元素指定像素的第一列中心的 x 坐标,第二个元素指定像素最后一列中心的 x 坐标。类似地,YData 的两个元素指定 了第一行和最后一行中心的y坐标。对于包含M行、 N列的图像来说,默认的XData向量是[1, N], 默认的 YData 向量是[1, M]。

编写代码:

f=imread('D:\数字图像处理\第五章学习\face.jpg');
imshow(f,'XData',[-20,20],'YData',[-10,10])
axis on
xlabel x
ylabel y

代码运行效果如下:

放大图像左上角

axis([8 8.5 0.8 1.1])

用上面命令放大图像左上角之后,发现已不再是方形。

5.1 输出图像位置

函数 imtransform 可以通过使用附加参数来提供输出空间中图像的位置信息。调用语法是:

[g, xdata, ydata] = imtransform(f, tform)

当使用 imshow 显示图像时,第 2个和第 3 个参数可以像XData和 YData 参数那样来使用。

用旋转和平移来研究如何在相同的坐标系统中与输入图像一起显示和定位输出图像,以使用标以记号的轴和标记显示原始图像开始。

编写代码:

f=imread('D:\数字图像处理\第五章学习\cat5.jpg');
imshow(f)
hold on                              %调用hold on,以便后续的绘图或显示命令不清除该图
theta=3*pi/4;  
T=[cos(theta) sin(theta) 0
   -sin(theta) cos(theta) 0
   0           0          1];
tform=maketform('affine',T);
[g,xdata,ydata]=imtransform(f,tform,'FillValue',255);    %imtransform使图像旋转3*pi/4弧度
imshow(g,'XData',xdata,'YData',ydata)
axis auto                            %使用axis函数自动扩展轴的限制,以便两幅图像同时可见
axis on                              %打开标有记号的轴和标记

代码运行效果如下:

在结果中可以看到,仿射变换关于系统坐标原(0,0)旋转图像。 接下来,检验平移,这是比旋转简单的仿射变换。

编写代码:

f=imread('D:\数字图像处理\第五章学习\cat5.jpg');
T=[1 0 0;0 1 0;500 200 1];    %以构成仿射变换tform结构开始,向右移动500,向下移动200
tform=maketform('affine',T);
g=imtransform(f,tform);
imshow(g)

代码运行效果如下:

5.2 控制输出网格

前面说明了如何用 xdata 和 ydata 参数形象化平移的效果,从输出 imtransform 到输 入 imshow。另一个方法是在输出空间中用imtransform 直接控制像素网格。通常,imtransform 在输出空间中用下列步骤定位和计算输出图像:
(1) 决定输入图像的边界矩形。
(2) 在边界矩形上把点变换到输出空间中。
(3) 计算变换后输出空间中点的边界矩形。
(4) 计算位于输出空间中、边界矩形内的网格上的输出图像像素。

如下自定义函数说明了 xdata 和 ydata 参数的使用。这是 imtransform 的另一个变异, 总使用像输出空间矩形那样的输入空间矩形。这样一来,输入和输出图像的位置便可更直接地 进行比较:

function g=imtransform2(f,varargin)
% IMTRANSFORM2 2-D image transformation with fixed output location
% G=IMTRANSFORM2(F,TFORM,...)applies a 2-D geometric
% transformation to an image.IMTRANSFORM2 fixes the output image
% location to cover the same region as the input image.
% IMTRANSFORM2 takes the same set of optional parameter/value
% pairs as IMTRANSFORM.
[M,N]=size(f);
xdata=[1 N];
ydata=[1 M];
g=imtransform(f,varargin{:},'XData',xdata,'YData',ydata);

函数 imtransform2 是封装函数的典型示例。封装函数接受输入,有可能改进它们或把它们相加,然后把它们传递给另一个函数。

针对几个几何变换,比较imtransform 和imtransform2 的输出。

f=imread('D:\数字图像处理\第五章学习\cat5.jpg');
tform1=maketform('affine',[1 0 0;0 1 0;300 500 1]);
g1=imtransform2(f,tform1,'FillValue',200);
h1=imtransform(f,tform1,'FillValue',200);
tform2=maketform('affine',[0.25 0 0;0 0.25 0;0 0 1]);
g2=imtransform2(f,tform2,'FillValues',200);
h2=imtransform(f,tform2,'FillValues',200);
subplot(2, 3, 1),imshow(f);title('(a)原图像');
subplot(2, 3, 2),imshow(g1);title('(b)用函数 imtransform2 平移图像');
subplot(2, 3, 3),imshow(h1);title('(c)用 imtransform 和默认参数平移图像');  
figure;imshow(g2),title('(d)用imtransform2 缩放图像');  
figure;imshow(h2),title('(e)用imtransform和默认参数缩放图像');  

代码运行效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实验结论:从图可以看出(b)显示了使用imtransform2 的结果,与(a)进行比较,可以很容易地 看出平移的效果。注意图 (b),输出图像的一部分已被切掉,而在图( c )中,显示了使用imtransform的结果,全部输出图像是可见的,但是失去了平移效果。两个输出(图 (d)和(e))显示了全部输出图像。从 imtransform2 的输出可以看到,虽然比变换后的图像大,但填充的像素灰度很协调,而来自函数 imtransform 的输出刚好包含变换后的图像。

(六)图像内插

对于数字图像,f的值仅在整数值位置是知道的。使用这些已知的值去估计在非整数值位 置的/是内插的典型示例— —用离散数据构建连续定义的函数。内插具有很长的历史,很多年 来,已经提出了众多的内插方法。在信号处理文献中,内插常常被解释为拥有两个设计步骤的重取样过程:
(1) 把连续变换离散化,把拥有连续域定义的函数尸转换为在离散域定义的函数f。
(2) 在离散位置估计f’的值。
当知道f的取样是有规律的间隔时,这个解释最有用。从离散到连续的变换步骤,可以用缩放和移动过的函数的求和来明确地表达,该函数叫做内插核。

一些常用的内插核:盒状核hgh_g(x)、三角核hT(x)h_T(x)

盒装核由下式定义:
在这里插入图片描述
三角核由下式定义:
在这里插入图片描述

6.1 二维内插

在图像处理中,最常用的二维内插方法是把问题分成一系列的多个一维内插任务。
在这里插入图片描述
上图说明了使用几个特定值的过程。在这个过程中,f’(2.6,1.4)是由围绕着f(x,y)的样本用一维序列的线性内插得到的:
(1) 用f(2,1)和f(3,1)之间的线性内插决定f’(2.6,1.0)。
(2) 用f(2, 2)和f(3, 2)之间的线性内插决定f’(2.6,2.0)。
(3) 用f’(2.6,1.0)和f’(2.6,2.0)之间的线性内插决定f’(2.6,1.4)。
用一系列的一维线性内插实现二维内插的处理方法被称为双线性内插。类似地,双三次内插是用一系列的一维三次内插完成的。

6.2 内插方法的比较

不同内插方法在计算速度和输出质量方面是不同的。用于说明赞成和反对不同内插方法的经典测试是重复旋转。

针对最近邻、双线性和双三次内插,用 reprotate 比较计算速度和图像质量。 函数连续旋转输入12次,由调用者指定要使用的内插方法。首先,用 timeit 记录每一种方法的时间:

timeit(@() reprotate(f, 'nearest'));

在这里插入图片描述

timeit(@() reprotate(f,'bilinear'));

在这里插入图片描述

timeit(@() reprotate(f,'bicubic'));

在这里插入图片描述
正如预期的那样,最近邻内插最快,双三次内插最慢。

f=imread('D:\数字图像处理\第五章学习\cat5.jpg');
f=rgb2gray(f);
subplot(2, 2, 1), imshow(f);title('(a)原图像');
subplot(2, 2, 2), imshow(reprotate(f,'nearest'));title('(b)最近邻内插');
subplot(2, 2, 3), imshow(reprotate(f,'bilinear'));title('(c)双线性内插');
subplot(2, 2, 4), imshow(reprotate(f,'bicubic'));title('(d)双三次内插');   %必须是灰度图像

实验结论:由图可以看出(b)中的最近邻结果显示出存在很大的“锯齿”边缘失真。( c ) 中的双线性内插结果有较平滑的边缘,但整个外观有点模糊。(d)中的双三次内插看起来最好,比起双线性内插有更平滑的边缘和更少的模糊。注意,对于重复的 12 次旋转,只有图像中心处的像素仍属于非边界像素。保留的像素是黑色的。

(七) 图像配准

几何变换最重要的图像处理应用之一是图像配准。图像配准方法寻求对准两幅或多幅相同场景的图像。例如,可能是对准不同时间获取的图像,计量的时差可能是一个月或一年。在同时获取多幅图像时,会有不同的情节发生,但采用不同的手段。例如,为了测量场景 深度,在不同位置的两台摄像机可能同时获取相同场景同时发生的图像。有时,图像来自不同的设备。两幅卫星图像可能在分辨率和空间特性上都不同。 在这种情况下,目标常常是把个别的图像融合为单一图像以增强图像的可视性。

7.1 配准处理

图像配准方法通常由下列基本步骤组成:
(1) 检测特征
(2) 匹配相应的特征
(3) 推断几何变换
(4) 通过几何变换用另一幅图像对准一幅图像
一幅图像的特征可以是图像的任何部分,可以是两幅图像中潜在的、可识别和可定位的部 分。例如,特征可以是点、线或角。一旦选择了特征,就要匹配特征。图像配准方法可以是手工的或自动 的,这取决于特征检测和配准是由人辅助的还是用自动算法执行。

7.2 使用 cpselect 的手工特征选择和匹配

图像处理工具箱为图像特征使用“控制点”这一术语。工具箱为手工选择和匹配被配准的 一对图像的相应控制点提供了名为控制点选择工具(cpselect)的GUI (图形用户界面)。把被对 准图像的文件名作为输入参量发送到 cpselect, 例如:

cpselect('D:\数字图像处理\第五章学习\cat5.jpg’ , 'D:\数字图像处理\第五章学习\cat.jpg') 

作为另一选择,也可以首先把图像读进 MATLAB变量,然后再传送给 cpselect:

f=imread('D:\数字图像处理\第五章学习\cat5.jpg');
g = imread('D:\数字图像处理\第五章学习\cat.jpg');
cpselect(f, g)

cpselect工具可帮助在大图像中导航(放大、摇镜头和滚动),可以选择特征(控制点),并用鼠标在图像上点击以与其他控制点配对。

代码运行效果如下:
在这里插入图片描述

7.3 使用 cp2tform 推断变换参数

一旦特征对被识别和匹配,图像配准处理的下一步就是确定几何变换函数。通常是首先选 择某个特定的变换模型,然后估计必需的参数。例如,可能决定某个仿射变换是合适的,并且 用相应的特征对推导仿射变换矩阵。图像处理工具箱为从特征对推断几何变换参数提供了函数 cp2tforrrio cp2tform 的语法是:

tform = cp2tform (input—points, base_points, transformtype)

参数 input_points 和 base_points 是两个PX2大小的包含相应特征位置的矩阵。

7.4 基于区域的配准

对于外在特征选择和匹配的可供选择的方法是基于区域的配准。在基于区域的配准中,称作模板图像的一幅图像被移动以覆盖第二幅图像的每个位置。在每个位置,计算基于区域的相似性度量。如果在相似性度量中,在某个特定位置找到明显的峰值,就可以说模板图像在第二幅图像的某个特定位置匹配。 使用函数 normxcorr2 在图像中定位模板,调用语法是:

g = normxcorr2(template, f)
f=rgb2gray(imread('D:\数字图像处理\第五章学习\cat5.jpg'));
w = rgb2gray(imread('D:\数字图像处理\第五章学习\cat.jpg'));
g = normxcorr2(w, f);
subplot(1, 3, 1), imshow(f);title('(a)原图像');
subplot(1, 3, 2), imshow(w);title('(b)模板');
subplot(1, 3, 3), imshow(abs(g));title('(c)归一化相关的绝对值');

在这里插入图片描述
实验结论:这个例子使用 normxcorr2 在模板和图像之间寻找最好匹配的位置。首先,读入模板和图像,然后使用 normxcorr2 计算和显示归一化互相关,图( c )显示了归一化互相关图像,最亮的点,该点指出了图像和模板之间的匹配。 除了归一化互相关外,近几年来,在图像处理文献中还提出了许多其他基于区域的相似性 度量。例如平方差的和、绝对差的和。不同的度量,因素也不同。在简单的情况下,用归一化互相关或其他相似性度量的模板匹配也可用于匹配两幅重叠的图像。

2016-04-12 15:38:54 u013524303 阅读数 1577
  • 计算机图形学基础

    该课程是计算机图形领域的基础的课程,包含了计算机图形学的数学原理,例如在计算机图形学中的重要坐的标变换的原理。该课程也是游戏开发、VR/AR等开发的基础课程。在该课程中除了讲授数学原理,还包括了C++实践,实现3D数学类。 该课程的主要内容包括:向量、向量空间、矩阵、矩阵空间、仿射空间、齐次坐标系、仿射变换、刚体变换、四元数等。

    2774 人正在学习 去看看 张赐

在《数字图象处理》中提供了基于
这里写图片描述
图像仿射矩阵T。图像旋转、偏移变换等变换原理相同,根据这些在自己实现时,可分为三个步骤:

  1. 坐标转换
    这些变换是以图像的中心为坐标原点(O’(x’,y’)),而图片原本设定是以图像左上角为坐标原点(O(x,y))。所以要进行坐标的变换。
    这里写图片描述
    用矩阵的方式表示:
    这里写图片描述
    逆运算:
    这里写图片描述

  2. 图像变换
    变换公式:
    这里写图片描述
    逆运算:
    这里写图片描述

  3. 坐标还原
    因为此时,坐标原点是在图像的中心,为方便操作需要将坐标原点重新变换到图像左上角。
    从旋转后到旋转前的坐标变换为:
    这里写图片描述
    (w’,h’是旋转后的图像的宽高);
    而逆运算为:
    这里写图片描述 (1)

下面是旋转的最邻近内插法和双线性内插法的实现:
其实在实现中,最重要的是求出变换后的图像宽高,再根据(1)式推出变换后的每一个像素点对应的原图像坐标。根据(1)式计算原图像像素点坐标x,y时,结果中分别会有一个常量,用dx,dy表示。

void nearestInterpolation(Mat &src, Mat &dst, float dx, float dy, double theta);
int mainqqw()
{
    cv::Mat src = imread("D:/xitong/picture/rain.jpg");
    namedWindow("orginal");
    imshow("orginal", src);
    int srcwidth = src.cols;
    int srcheigh = src.rows;
    /*旋转角度*/
    double theta = 30.0f*3.1415926 / 180.0f;
    /*
        转换坐标原点到图像中心
                ∧y
                |
            0   |   1
                |
        --------o--------->x
                |
            2   |   3
                |
    */
    float srcX[4], srcY[4];
    srcX[0] = (float)(-((srcwidth - 1) / 2));
    srcX[1] = (float)((srcwidth - 1) / 2);
    srcX[2] = (float)(-(srcwidth - 1) / 2);
    srcX[3] = (float)((srcwidth - 1) / 2);
    srcY[0] = (float)((srcheigh - 1) / 2);
    srcY[1] = (float)((srcheigh - 1) / 2);
    srcY[2] = (float)(-(srcheigh - 1) / 2);
    srcY[3] = (float)(-(srcheigh - 1) / 2);

    /*
        旋转后的图像坐标,此时坐标原点依然是旋转中心
    */
    float dstX[4], dstY[4];
    for (int i = 0; i < 4; i++)
    {
        dstX[i] = cos(theta)*srcX[i] + sin(theta)*srcY[i];
        dstY[i] = -sin(theta)*srcX[i] + cos(theta)*srcY[i];
    }

    /*
        ==>旋转后图像长宽
    */
    int dstwidth = (max(fabs(dstX[3] - dstX[0]), fabs(dstX[2] - dstX[1])) + 0.5);
    int dstheigh = (max(fabs(dstY[3] - dstY[0]), fabs(dstY[2] - dstY[1])) + 0.5);

    /*Mat dst = Mat(Size(src.rows * 2, src.cols * 2), src.type(), Scalar::all(0));
    nearestInterpolation(src, dst, 0.5);*/
    Mat dst;
    dst.create(dstheigh, dstwidth, src.type());

    //(1)式在运算后的常量,为运算方便提前的出结果
    float dx = -0.5*dstwidth*cos(theta) - 0.5*dstheigh*sin(theta) + 0.5*srcwidth;
    float dy = 0.5*dstwidth*sin(theta) - 0.5*dstheigh*cos(theta) + 0.5*srcheigh;

    nearestInterpolation(src, dst, dx, dy, theta);
    waitKey();
    return 0;
}

/*
    最邻近内插旋转
*/
void nearestInterpolation(Mat &src, Mat &dst, float dx, float dy, double theta)
{
    int x, y;
    for (int i = 0; i < dst.rows; i++)
    {
        for (int j = 0; j < dst.cols; j++)
        {
            /*
                根据(1)式,推出对应原图像 像素坐标的运算结果
            */
            x = cvFloor(float(j)*cos(theta) + float(i)*sin(theta) + dx);
            y = cvFloor(float(-j)*sin(theta) + float(i)*cos(theta) + dy);
            if ((x < 0) || (x >= src.cols) || (y < 0) || (y >= src.rows))
            {
                if (src.channels() == 3)
                    dst.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
                if (src.channels() == 1)
                    dst.at<uchar>(i, j) = 0;
            }
            else
            {
                if (src.channels() == 3)
                    dst.at<Vec3b>(i, j) = src.at<Vec3b>(y, x);
                if (src.channels() == 1)
                    dst.at<uchar>(i, j) = src.at<uchar>(y, x);
            }

        }
    }
    namedWindow("最邻近内插旋转");
    imshow("最邻近内插旋转", dst);
}
/*
    双线性内插法旋转
*/
void bilinearRotate(Mat &src, Mat &dst, float dx, float dy, double theta)
{
    float fu, fv;
    int x, y;
    Vec3b point[4];
    uchar upoint[4];
    for (int j = 0; j < dst.rows; j++)
    {
        for (int i = 0; i < dst.cols; i++)
        {
            fu = float(j)*cos(theta) + float(i)*sin(theta) + dx;
            fv = float(-j)*sin(theta) + float(i)*cos(theta) + dy;
            x = cvFloor(fu);
            y = cvFloor(fv);
            fu -= x;
            fv -= y;

            if ((x < 0) || (x >= src.cols-1) || (y < 0) || (y >= src.rows-1))
            {
                if (src.channels() == 3)
                    dst.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
                if (src.channels() == 1)
                    dst.at<uchar>(i, j) = 0;
            }
            else
            {
                if (src.channels() == 3)
                {
                    point[0] = src.at<Vec3b>(y, x);
                    point[1] = src.at<Vec3b>(y + 1, x);
                    point[2] = src.at<Vec3b>(y, x + 1);
                    point[3] = src.at<Vec3b>(y + 1, x + 1);
                    dst.at<Vec3b>(i, j) = (1 - fu)*(1 - fv)*point[0] + (1 - fu)*(fv)*point[1] + (1 - fv)*(fu)*point[2] + fu*fv*point[3];
                }

                if (src.channels() == 1)
                {
                    upoint[0] = src.at<uchar>(y, x);
                    upoint[1] = src.at<uchar>(y + 1, x);
                    upoint[2] = src.at<uchar>(y, x + 1);
                    upoint[3] = src.at<uchar>(y + 1, x + 1);
                    dst.at<uchar>(i, j) = (1 - fu)*(1 - fv)*upoint[0] + (1 - fu)*(fv)*upoint[1] + (1 - fv)*(fu)*upoint[2] + fu*fv*upoint[3];
                }
            }
        }
    }
    namedWindow("双线性内插法旋转");
    imshow("双线性内插法旋转", dst);
}
没有更多推荐了,返回首页