梳理一下常见的图像几何变换及校正方式。参考资料来自《Computer Vision:Algorithms and Applications 》Richard Szeliski
图像几何变换
由于拍摄角度的不固定,图像出现几何变换,平移、旋转、尺度、仿射、投影等。如车牌检测的预处理,要将车牌校正,变成一个矩形。
- translation
2D的平移,x′=x+tx′=x+t
x¯′=[I0t1]x¯x¯′=[It01]x¯ rotation+translation
2D刚性运动,x′=Rx+tx′=Rx+t
R=[ cosθsinθ−sinθcosθ]R=[ cosθ−sinθsinθcosθ]
正交旋转矩阵,欧式距离保持不变scaled rotation
“相似”变化,x′=sRx+tx′=sRx+t, ss代表尺度因子
相似变换保持直线间的夹角不变stretch/squash
改变图像的aspect ratiox′=sxx+txx′=sxx+txy′=syy+tyy′=syy+tyaffine
仿射变换写作x′=Ax¯x′=Ax¯,AA是一个的矩阵
x′=⎡⎣⎢a00a10a20a01a11a21a02a12a22a03a13a23⎤⎦⎥x¯x′=[a00a01a02a03a10a11a12a13a20a21a22a23]x¯
平行线和平行平面在经过仿射变换之后仍然保持平行。
- projective
投影变换,3D透视变换
直线在经过投影变换之后还是直线
- 双线性内插
图像校正
存在明显特征的图像,可以提取特征点,然后配准求变换矩阵。注意选用的特征是否本身具备尺度、旋转不变性等。
如果只需要进行图像旋转校正,可以使用霍夫变换检测直线然后计算角度。我的理解,通过ρ=x∗cos(θ)+y∗sin(θ)ρ=x∗cos(θ)+y∗sin(θ),把每个通过点(x,y)(x,y)的直线就变成了正弦曲线(ρ,θ)(ρ,θ)。在参数空间中,找到(ρ,θ)(ρ,θ)的极值点,就对应一条直线。
function H = standardHoughTransform(BW,theta,rho)
coder.inline('always');
coder.internal.prefer_const(BW,theta,rho);
rhoLength = coder.internal.indexInt(length(rho));
thetaLength = coder.internal.indexInt(length(theta));
firstRho = rho(1);
[numRow,numCol] = size(BW);
% Allocate space for H and initialize to 0
H = zeros(rhoLength,thetaLength);
% Allocate space for cos/sin lookup tables
cost = coder.nullcopy(zeros(thetaLength,1));
sint = coder.nullcopy(zeros(thetaLength,1));
% Pre-compute the sin and cos tables
for i = 1:thetaLength
% Theta is in radians
cost(i) = cos(theta(i) * pi/180);
sint(i) = sin(theta(i) * pi/180);
end
% Compute the factor for converting back to the rho matrix index
slope = double(rhoLength-1) / double(rho(rhoLength) - firstRho);
% Compute the Hough transform
for n = 1:numCol
for m = 1:numRow
if BW(m,n) % if pixel is on
for thetaIdx = 1:thetaLength
% rho = x*cos(theta) + y*sin(theta)
myRho = (n-1) * cost(thetaIdx) + (m-1) * sint(thetaIdx);
% convert to bin index
rhoIdx = roundAndCastInt(slope*(myRho - firstRho)) + 1;
% accumulate
H(rhoIdx,thetaIdx) = H(rhoIdx,thetaIdx)+1;
end
end
end
end
%--------------------------------------------------------------------------
function y = roundAndCastInt(x)
coder.inline('always');
coder.internal.prefer_const(x);
% Only works if x >= 0
y = coder.internal.indexInt(x+0.5);
得到直线的斜率之后,将图片旋转、“掰正”。这时候图像存在黑色背景,然后根据斜率的符号确定图像的旋转方向,进而得到图形的有效区域。
直线检测也可以通过拟合得到,基于最小二乘和RANSAC得到直线方程。