单应性变换是将一个平面内的点映射到另一个平面内的二维投影变换。在这里,平面是指图像或者三维中的平面表面。本质上,按照下面的方程映射二维中的点(齐次坐标意义下): [ x ′ y ′ w ′ ] = [ h 1 h 2 h 3 h 4 h 5 h 6 h 7 h 8 h 9 ] [ x y w ] 或 x ′ = H x \begin{bmatrix}x'\\y'\\w'\end{bmatrix}=\begin{bmatrix}h_1&h_2&h_3\\h_4&h_5&h_6\\h_7&h_8&h_9\end{bmatrix}\begin{bmatrix}x\\y\\w\end{bmatrix}\quad\quad\text{或}\quad\mathbf{x}'=H\mathbf{x} x′y′w′=h1h4h7h2h5h8h3h6h9xyw或x′=Hx
仿射变换 变换名称 仿射矩阵 坐标公式 恒等变换 [ 1 0 0 0 1 0 0 0 1 ] x = v y = w 尺度变换 [ c x 0 0 0 c y 0 0 0 1 ] x = c x v y = c y w 旋转变换 [ cos θ sin θ 0 − sin θ cos θ 0 0 0 1 ] x = v cos θ − w sin θ y = v sin θ + w cos θ 平移变换 [ 1 0 0 0 1 0 t x t y 1 ] x = v + t x y = w + t y 垂直偏移变换 [ 1 0 0 s v 1 0 0 0 1 ] x = v + s v w y = w 水平偏移变换 [ 1 s h 0 0 1 0 0 0 1 ] x = v y = s h v + w 仿射变换 \\ \begin{array} {ccc} \hline 变换名称 & 仿射矩阵 & 坐标公式\\ \hline 恒等变换 & \begin{bmatrix}1&0&0\\ 0&1&0\\ 0&0&1\end{bmatrix} &\begin{gathered}x=v \\y=w \end{gathered}\\ 尺度变换 & \begin{bmatrix}c_x&0&0\\ 0&c_y&0\\ 0&0&1\end{bmatrix} & \begin{gathered} x=c_xv \\ y=c_yw \end{gathered} \\ 旋转变换 & \begin{bmatrix}\cos\theta&\sin\theta&0\\ -\sin\theta&\cos\theta&0\\ 0&0&1\end{bmatrix} & \begin{gathered} x=v\cos\theta-w\sin\theta \\ y=v\sin\theta+w\cos\theta \end{gathered} \\ 平移变换 & \begin{bmatrix}1&0&0\\ 0&1&0\\ t_x&t_y&1\end{bmatrix} &\begin{gathered}x=v+t_x \\y=w+t_y \end{gathered}\\ 垂直偏移变换 & \begin{bmatrix}1&0&0\\ s_v&1&0\\ 0&0&1\end{bmatrix} &\begin{gathered}x=v+s_vw \\y=w \end{gathered}\\ 水平偏移变换 & \begin{bmatrix}1&s_h&0\\ 0&1&0\\ 0&0&1\end{bmatrix} &\begin{gathered}x=v \\y=s_hv+w \end{gathered}\\ \hline \end{array} 仿射变换变换名称恒等变换尺度变换旋转变换平移变换垂直偏移变换水平偏移变换仿射矩阵100010001cx000cy0001cosθ−sinθ0sinθcosθ000110tx01ty0011sv0010001100sh10001坐标公式x=vy=wx=cxvy=cywx=vcosθ−wsinθy=vsinθ+wcosθx=v+txy=w+tyx=v+svwy=wx=vy=shv+w
import math
import cv2
import numpy as np
import matplotlib.pyplot as plt
defplt_show(*args):for ttt inrange(len(args)):
img = args[ttt]if(len(img.shape)==3):
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)elif(len(img.shape)==2):
img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
plt.subplot(231+ttt), plt.imshow(img)
img = cv2.imread('cv.png',0)# cv2.imwrite('cv.png',img)
n, m = img.shape
n +=100
m +=100
points =[]for i inrange(len(img)):for j inrange(len(img[0])):if img[i][j]:
points.append([i, j])
angle =3.14/6
mats =[np.array([1,0,0,0,1,0,0,0,1]).reshape(3,3),# 恒等变换
np.array([1.2,0,0,0,1.2,0,0,0,1]).reshape(3,3),# 尺度变换
np.array([math.cos(angle), math.sin(angle),0,# 旋转变换-math.sin(angle), math.cos(angle),0,0,0,1]).reshape(3,3),
np.array([1,0,0,0,1,0,30,50,1]).reshape(3,3),# 平移变换
np.array([1,0,0,1.1,1,0,0,0,1]).reshape(3,3),# 垂直变换
np.array([1,1.1,0,0,1,0,0,0,1]).reshape(3,3),# 水平变换]
imgs =[]for mat in mats:
img_t = np.zeros([n, m])for v, w in points:
x, y, z = np.dot([v, w,1], mat)try:# 避免新坐标越界
img_t[int(x)][int(y)]=254except:pass
imgs.append(img_t.astype(np.uint8))
plt_show(imgs[0], imgs[1], imgs[2], imgs[3], imgs[4], imgs[5])
上图分别是:原图、放大、旋转、平移、垂直偏移、水平偏移。这些是基本的仿射变换。
3.1.1 直接线性变换算法
单应性矩阵可以由两幅图像(或者平面)中对应点对计算出来。一个完全射影变换具有 8 个自由度。根据对应点约束,每个对应点对可以写出两个方程,分别对应于 x 和 y 坐标。因此,计算单应性矩阵 H 需要4个对应点对。DLT(Direct Linear Transformation,直接线性变换)是给定4个或者更多对应点对矩阵,来计算单应性矩阵 H 的算法。我们可以得到下面的方程: [ − x 1 − y 1 − 1 0 0 0 x 1 x 1 ′ y 1 x 1 ′ x 1 ′ 0 0 0 − x 1 − y 1 − 1 x 1 y 1 ′ y 1 y 1 ′ y 1 ′ − x 2 − y 2 − 1 0 0 0 x 2 x 2 ′ y 2 x 2 ′ x 2 ′ 0 0 0 − x 2 − y 2 − 1 x 2 y 2 ′ y 2 y 2 ′ y 2 ′ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ] [ h 1 h 2 h 3 h 4 h 5 h 5 h 6 h 7 h 8 h 9 ] = 0 \begin{bmatrix}-x_1&-y_1&-1&0&0&0&x_1x_1^{\prime}&y_1x_1^{\prime}&x_1^{\prime}\\0&0&0&-x_1&-y_1&-1&x_1y_1^{\prime}&y_1y_1^{\prime}&y_1^{\prime}\\-x_2&-y_2&-1&0&0&0&x_2x_2^{\prime}&y_2x_2^{\prime}&x_2^{\prime}\\0&0&0&-x_2&-y_2&-1&x_2y_2^{\prime}&y_2y_2^{\prime}&y_2^{\prime}\\&\vdots&&\vdots&\vdots&\vdots&\vdots&\vdots\end{bmatrix}\begin{bmatrix}h_1\\h_2\\h_3\\h_4\\h_5\\h_5\\h_6\\h_7\\h_8\\h_9\end{bmatrix}=\mathbf{0} −x10−x20−y10−y20⋮−10−100−x10−x2⋮0−y10−y2⋮0−10−1⋮x1x1′x1y1′x2x2′x2y2′⋮y1x1′y1y1′y2x2′y2y2′⋮x1′y1′x2′y2′h1h2h3h4h5h5h6h7h8h9=0 即 Ah=0。
我们可以使用 SVD(Singular Value Decomposition,奇异值分解)算法找到 H 的最小二乘解。下面是该算法的代码
import numpy as np
defH_from_points(fp, tp):""" 使用线性 DLT 方法,计算单应性矩阵 H,使 fp 映射到 tp。点自动进行归一化 """if fp.shape != tp.shape:raise RuntimeError('number of points do not match')# 对点进行归一化(对数值计算很重要)# --- 映射起始点 ---
m = np.mean(fp[:2], axis=1)
maxstd = np.max(np.std(fp[:2], axis=1))+1e-9
C1 = np.diag([1/maxstd,1/maxstd,1])
C1[0][2]=-m[0]/maxstd
C1[1][2]=-m[1]/maxstd
fp = np.dot(C1, fp)# --- 映射对应点 ---
m = np.mean(tp[:2], axis=1)
maxstd = np.max(np.std(tp[:2], axis=1))+1e-9
C2 = np.diag([1/maxstd,1/maxstd,1])
C2[0][2]=-m[0]/maxstd
C2[1][2]=-m[1]/maxstd
tp = np.dot(C2, tp)# 创建用于线性方法的矩阵,对于每个对应对,在矩阵中会出现两行数值
nbr_correspondences = fp.shape[1]
A = np.zeros((2*nbr_correspondences,9))for i inrange(nbr_correspondences):
A[2*i]=[-fp[0][i],-fp[1][i],-1,0,0,0,
tp[0][i]*fp[0][i], tp[0][i]*fp[1][i], tp[0][i]]
A[2*i+1]=[0,0,0,-fp[0][i],-fp[1][i],-1,
tp[1][i]*fp[0][i], tp[1][i]*fp[1][i], tp[1][i]]
U, S, V = np.linalg.svd(A)
H = V[8].reshape((3,3))# 反归一化
H = np.dot(np.linalg.inv(C2), np.dot(H, C1))# 归一化,然后返回return H / H[2,2]
因为算法的稳定性取决于坐标的表示情况和部分数值计算的问题,所以归一化操作非常重要。接下来我们使用对应点对来构造矩阵 A。最小二乘解即为矩阵 SVD 分解后所得矩阵 V 的最后一行。该行经过变形后得到矩阵 H。然后对这个矩阵进行处理和归一化,返回输出
imname =['Univ'+str(i+1)+'.jpg'for i inrange(5)]
plt.figure(figsize=(15,8))for i inrange(5):
img=cv2.cvtColor(cv2.imread(imname[i]), cv2.COLOR_BGR2RGB)
plt.subplot(2,3,i+1)
plt.axis('off')
plt.imshow(img)
之后使用 SIFT 特征自动找到匹配对应
import cv2
sift = cv2.SIFT_create()
l ={}
d ={}for i inrange(5):
image = cv2.imread(imname[i], cv2.IMREAD_GRAYSCALE)
keypoints = sift.detect(image,None)
keypoints, descriptors = sift.compute(image, keypoints)
l[i], d[i]= keypoints, descriptors
matches ={}for i inrange(4):
bf = cv2.BFMatcher()
matches[i]= bf.knnMatch(d[i+1], d[i], k=2)
'''homograph.py'''from numpy import*from scipy import ndimage
classRansacModel(object):""" Class for testing homography fit with ransac.py from
http://www.scipy.org/Cookbook/RANSAC"""def__init__(self,debug=False):
self.debug = debug
deffit(self, data):""" Fit homography to four selected correspondences. """# transpose to fit H_from_points()
data = data.T
# from points
fp = data[:3,:4]# target points
tp = data[3:,:4]# fit homography and returnreturn H_from_points(fp,tp)defget_error( self, data, H):""" Apply homography to all correspondences,
return error for each transformed point. """
data = data.T
# from points
fp = data[:3]# target points
tp = data[3:]# transform fp
fp_transformed = dot(H,fp)# normalize hom. coordinates
fp_transformed = normalize(fp_transformed)# return error per pointreturn sqrt(sum((tp-fp_transformed)**2,axis=0))defH_from_ransac(fp,tp,model,maxiter=1000,match_theshold=10):""" Robust estimation of homography H from point
correspondences using RANSAC (ransac.py from
http://www.scipy.org/Cookbook/RANSAC).
input: fp,tp (3*n arrays) points in hom. coordinates. """from PCV.tools import ransac
# group corresponding points
data = vstack((fp,tp))# compute H and return
H,ransac_data = ransac.ransac(data.T,model,4,maxiter,match_theshold,10,return_all=True)return H,ransac_data['inliers']defH_from_points(fp,tp):""" Find homography H, such that fp is mapped to tp
using the linear DLT method. Points are conditioned
automatically. """if fp.shape != tp.shape:raise RuntimeError('number of points do not match')# condition points (important for numerical reasons)# --from points--
m = mean(fp[:2], axis=1)
maxstd =max(std(fp[:2], axis=1))+1e-9
C1 = diag([1/maxstd,1/maxstd,1])
C1[0][2]=-m[0]/maxstd
C1[1][2]=-m[1]/maxstd
fp = dot(C1,fp)# --to points--
m = mean(tp[:2], axis=1)
maxstd =max(std(tp[:2], axis=1))+1e-9
C2 = diag([1/maxstd,1/maxstd,1])
C2[0][2]=-m[0]/maxstd
C2[1][2]=-m[1]/maxstd
tp = dot(C2,tp)# create matrix for linear method, 2 rows for each correspondence pair
nbr_correspondences = fp.shape[1]
A = zeros((2*nbr_correspondences,9))for i inrange(nbr_correspondences):
A[2*i]=[-fp[0][i],-fp[1][i],-1,0,0,0,
tp[0][i]*fp[0][i],tp[0][i]*fp[1][i],tp[0][i]]
A[2*i+1]=[0,0,0,-fp[0][i],-fp[1][i],-1,
tp[1][i]*fp[0][i],tp[1][i]*fp[1][i],tp[1][i]]
U,S,V = linalg.svd(A)
H = V[8].reshape((3,3))# decondition
H = dot(linalg.inv(C2),dot(H,C1))# normalize and returnreturn H / H[2,2]defHaffine_from_points(fp,tp):""" Find H, affine transformation, such that
tp is affine transf of fp. """if fp.shape != tp.shape:raise RuntimeError('number of points do not match')# condition points# --from points--
m = mean(fp[:2], axis=1)
maxstd =max(std(fp[:2], axis=1))+1e-9
C1 = diag([1/maxstd,1/maxstd,1])
C1[0][2]=-m[0]/maxstd
C1[1][2]=-m[1]/maxstd
fp_cond = dot(C1,fp)# --to points--
m = mean(tp[:2], axis=1)
C2 = C1.copy()#must use same scaling for both point sets
C2[0][2]=-m[0]/maxstd
C2[1][2]=-m[1]/maxstd
tp_cond = dot(C2,tp)# conditioned points have mean zero, so translation is zero
A = concatenate((fp_cond[:2],tp_cond[:2]), axis=0)
U,S,V = linalg.svd(A.T)# create B and C matrices as Hartley-Zisserman (2:nd ed) p 130.
tmp = V[:2].T
B = tmp[:2]
C = tmp[2:4]
tmp2 = concatenate((dot(C,linalg.pinv(B)),zeros((2,1))), axis=1)
H = vstack((tmp2,[0,0,1]))# decondition
H = dot(linalg.inv(C2),dot(H,C1))return H / H[2,2]defnormalize(points):""" Normalize a collection of points in
homogeneous coordinates so that last row = 1. """for row in points:
row /= points[-1]return points
defmake_homog(points):""" Convert a set of points (dim*n array) to
homogeneous coordinates. """return vstack((points,ones((1,points.shape[1]))))
# 将匹配转换成齐次坐标点的函数defconvert_points(j):
ndx =[i[0].queryIdx for i in matches[j]]
fp = np.array([l[j+1][i].pt for i in ndx])
ndx2 =[i[0].trainIdx for i in matches[j]]
tp = np.array([l[j][i].pt for i in ndx2])return fp, tp
# 估计单应性矩阵
model = RansacModel()
fp,tp = convert_points(0)
H_01 = H_from_ransac(fp,tp,model)[0]# im0 到 im1 的单应性矩阵
fp,tp = convert_points(1)
H_12 = H_from_ransac(fp,tp,model)[0]# im1 到 im2 的单应性矩阵
tp,fp = convert_points(2)# 注意:点是反序的
H_32 = H_from_ransac(fp,tp,model)[0]# im3 到 im2 的单应性矩阵
tp,fp = convert_points(3)# 注意:点是反序的
H_43 = H_from_ransac(fp,tp,model)[0]# im4 到 im3 的单应性矩阵
//关键字的使用探讨/*访问关键词private 只能在本类中访问public 只能在本工程中访问protected 只能在包中和子类中访问默认的 只能在包中访问*//*final 类 方法 变量 final 类 不能被继承 final 方法 不能被子类覆盖,但可以继承 final 变量 只能有一次赋值,赋值后不能改变 final 不能用来修饰构造方法*///this()
What’s new in Zabbix 2.0?
去年开始使用Zabbix的时候,是1.8.X的版本,今年Zabbix已经跨入了2.0的时代。看了2.0的release notes,和performance相关的有下面几个:
:: Performance improvements::Trigger related da
修改jboss端口
%JBOSS_HOME%\server\{服务实例名}\conf\bindingservice.beans\META-INF\bindings-jboss-beans.xml
中找到
<!-- The ports-default bindings are obtained by taking the base bindin
@echo off
::演示:删除指定路径下指定天数之前(以文件名中包含的日期字符串为准)的文件。
::如果演示结果无误,把del前面的echo去掉,即可实现真正删除。
::本例假设文件名中包含的日期字符串(比如:bak-2009-12-25.log)
rem 指定待删除文件的存放路径
set SrcDir=C:/Test/BatHome
rem 指定天数
set DaysAgo=1
HTML5的video和audio标签是用来在网页中加入视频和音频的标签,在支持html5的浏览器中不需要预先加载Adobe Flash浏览器插件就能轻松快速的播放视频和音频文件。而html5media.js可以在不支持html5的浏览器上使video和audio标签生效。 How to enable <video> and <audio> tags in