opencv图像处理框架
一.课程简介与环境配置
二.图像基本操作
(1)计算机眼中的视觉
1)计算机眼中图像是由一块块组成,每一块又由很多很多个像素点组成,一个像素点的值是在0到255之间,值越大就越亮。
2)RGB表示彩色图像的三个颜色通道(红绿蓝),一张彩色图像由很多个三通道的像素点组成,每个像素点在每个通道上的值都可在矩阵上表示出来,矩阵大小就是图像的长与宽(w与h表示)。
3)用opencv做简单的图像操作
<1>图像读取
先导入cv2与numpy
import cv2
import numpy as np
img=cv2.imread("图像完整路径")
用cv2直接读取到的图像是bgr格式的。
print(img)
print(img.shape)
发现它是3维矩阵,通常用(h,w,c)表示。读取到图像后,调imshow('名称',读到的图像)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
(2)视频的读取与处理
紧接上面内容,
1)读取灰度图(即一个通道的黑白图像),那就要在imread方法中增加一个参数,例
img=cv2.imread("图像完整路径",cv2.IMREAD_CRAYSCALE)
print(img)
print(img.shape)
2)保存图像,直接调imwrite('名称',读取到的图像)。例如cv2.imwrite('img',img)
3)视频是由每一帧每一帧(时间)的静止图像构成的,例如人脸识别也其实是对图像进行检测识别的。
调用cv2.VideoCapture('视频完整路径')可读取视频。例如
cv=cv2.VideoCapture('1.mp4');
cv2.VideoCapture()可以获取摄像头,用数字表示控制不同的设备,例用0,1表示。
4)判断读取的视频图像能否打开,能打开调用read方法返回true和返回每帧的图像,例如:
if cv.isOpened():
boo,img=cv.read()
while boo:
ret,frame=cv.read()
if frame is None:
break;
if ret==true:
gray= cv2.cvtColour(frame,cv2.COLOR_BGR2GRAY)
cv2.imshow('result',gray)
cv.release()
(3)ROI区域
1)截取部分图像数据(本例按切片方式)
img=cv2.imread("图像完整路径")
ca=img[0:50,0:200]
cv2.imshow('result',ca)
2)颜色通道提取
img=cv2.imread("图像完整路径")
b,g,r=cv2.split(img)
print(b)
print(b.shape)
如果不用split方法,那也可用切片方式获取,例:
b=img[::0]
print(b.shape)
3)组合图像
img=cv2.imread("图像完整路径")
b,g,r=cv2.split(img)
img2=cv2.merge((b,g,r))
print(img2.shape)
4)只保留某个颜色通道,可把其它通道置为0:
img=cv2.imread("图像完整路径")
img2=img.copy()
img2[::0]=0
img2[::2]=0
cv2.imshow('result',img2)
(4)边界填充
给定上,下,左,右四个值,调用cv2.copyMaskBorder方法来填充。例
img=cv2.imread("图像完整路径")
tsize,bsize,lsize,rsize=(50,50,50,50)
pad=cv2.copyMaskBorder(img,tsize,bsize,lsize,rsize,borderType=cv2.BORDER_REPLICATE)
上行最后的参数borderType值有好几个值,可查阅下。
(5)数值计算
1)shape值相同的像素点矩阵可直接相加,例二张图片直接用加号相加,如果两像素点的值加起来的数大于255,那么就用这个数除以256后取余的值为最终值。例如加起来是300,那最终值是300%256=44,例如:
img1=cv2.imread("图像完整路径")
img2=cv2.imread("图像完整路径")
img3=img1+img2
print(img3.shape)
cv2.imshow('result',img3)
2)另外一种就直接调cv2.add方法来做加法,它是会把加起后的值大于255的话,最终值就取255,例如
img3=cv2.add(img1,img2)
print(img3.shape)
cv2.imshow('result',img3)
3)图像融合
不同shape值需先进行resize操作,使它们的shape值相同,然后调用addWeighted方法进行两张图片融合,融合原理是z=k1*x1+k2*x2+b,其中x1与x2是指二张图像,k1与k2都是权重值,b是偏置项。例如:
dog_img=cv2.imread("图像完整路径")
cat_img=cv2.imread("图像完整路径")
print(dog_img.shape)
假设dog_img的shape是(414,500,3),现在把cat_img做resize操作,使与dog_img的shape一样。
cat_img=cv2.resize(cat_img,(500,414))
print(cat_img.shape)
res=cv2.addWeighted(cat_img,0.4,dog_img,0.6,0)
这里0.4,0.6,0值分别是k1,k2,b值
cv2.imshow('result',res)
上面的resize当中,如果不具体指定长宽值,那就都指定为0,然后后面加多fx与fy二个参数表示缩放的倍数值,例如
cat_img=cv2.resize(cat_img,(0,0),fx=2,fy=1.5)
print(cat_img.shape)
cv2.imshow('result',rescat_img)
三.阀值与平滑处理
(1)图像阀值
ret,dst=cv2.threshold(src,thresh,maxval,type)的参数说明:
dst:返回的图像
src:原始的单通道图像
thresh:阀值大小,通常可设置为127(255的一半)
maxval:最大值
type:包括5种类型的设置,不同类型就有不同的逻辑判断与计算。
例如:
dog_img=cv2.imread("图像完整路径")
ret,dst=cv2.threshold(dog_img,127,255,cv2.THRESH_BINARY)
print(dst.shape)
cv2.imshow('result',dst)
(2)图像平滑处理(图像滤波操作)
图像平滑处理其实是对图像各种滤波操作。
1)均值滤波:简单的平均卷积操作。例
img=cv2.imread("图像完整路径")
blur=cv2.blur(img,(3,3))
cv2.imshow('result',blur)
而blur=cv2.blur(img,(3,3))中的(3,3)是表示3*3的卷积大小,它是由全是1的3行3列的矩阵组成,然后在图像上按这个卷积核平移,因为这里是均值滤波,所以就是每个1与图像中的像素点值先做内积最后求平均(本例是除以9可以得到它的均值)
2)方框滤波:基本与均值滤波一样,可以选择归一化。调用的方法是boxFilter()。
例如:
img=cv2.imread("图像完整路径")
blur=cv2.boxFilter(img,-1,(3,3),normalize=true)
cv2.imshow('result',blur)
而blur=cv2.boxFilter(img,-1,(3,3),normalize=true)中的normalize=true时表示会做归一化(与均值滤波一样会算平均值,例除以9),而normalize=false时就不做归一化(不做平均了),如果累加和大于255时就设置为255(白色透明)。
(3)高斯滤波与中值滤波
1)高斯滤波
高斯是均值为零某个标准差的函数,即x轴上的值越接近0时,它的y值越大(图像上看越尖,可能性越大,感觉关系越紧密)。那我现在对3行3列像素点做高斯,假设最中间的那个权重设为1,而4个顶角上的权重为0.6(可想像成因为距离中间那个远一点,关系没那么紧密),而剩下4个对应的权重设为0.8(可想像成因为距离中间那个近一点,关系越紧密),这样算出来的值比均值滤波算出来的值也许更公布,高斯滤波其实就是把核积核中的值做权重分配,不再是全为1。
高斯滤波中卷积核里的数值是满足高斯分布的(即相当于更重视中间的)。例如:
img=cv2.imread("图像完整路径")
gaussian=cv2.GaussianBlur(img,(3,3),1)
cv2.imshow('result',gaussian)
2)中值滤波:把像素点值按从小到大排序好,然后取中间那个值。
img=cv2.imread("图像完整路径")
median=medianBlur(img,5)
cv2.imshow('result',median)
这里median=medianBlur(img,5)中的5是指5*5的卷积核,它对应就会取到排序后的第13个像素值。
3)用np把均值滤波,高斯滤波与中值滤波拼接展示在一起,例如:
res=np.hstack(blur,gaussian,median)
print(res)
cv2.imshow('result',res)
四.图像形态学操作
(1)腐蚀操作
调用erode方法做腐蚀操作,经过腐蚀操作后有价值的信息可能会越来越少,因为边缘上的像素点被卷积圈到时,背景会腐蚀掉前景中一部分值,这部分也许会变成背景了。例如
img=cv2.imread("图像完整路径")
假设读进来的这个图片是黑白图像的圆,背景是全黑的,前景的圆是全白的。
kenrel=np.ones((3,3),np.unit8)
res=cv2.erode(img,kenrel,iterations=1)
cv2.imshow('result',res)
当iterations这个迭代次数的值越大,腐蚀后的前景图会越小的感觉。
(2)膨胀操作
膨胀操作与腐蚀操作是逆操作。腐蚀感觉变瘦了,膨胀感觉变胖了。
调用dilate进行膨胀操作,例如
kenrel=np.ones((3,3),np.unit8)
img=cv2.dilate(res,kenrel,iterations=1)
cv2.imshow('img',img)
(3)开运算与闭运算
开运算是指腐蚀后与膨胀连在一起
1)开运算
开运算是先腐蚀再膨胀。调用morphologyEx方法实现开运算。例如:
img=cv2.imread("图像完整路径")
kenrel=np.ones((3,3),np.unit8)
img=cv2.morphologyEx(img,cv2.MORPH_OPEN,kenrel)
cv2.imshow('img',img)
2)闭运算
闭运算是先膨胀再腐蚀。调用morphologyEx方法实现闭运算。例如:
img=cv2.imread("图像完整路径")
kenrel=np.ones((3,3),np.unit8)
img=cv2.morphologyEx(img,cv2.MORPH_CLOSE,kenrel)
cv2.imshow('img',img)
(4)梯度计算
这里图像的梯度计算是用形态学中的膨胀后图像减去腐蚀后的图像,减去后会剩下边界信息(边缘信息)。所以还是调用morphologyEx方法,参数变成MORPH_GRADIENT,例如:
img=cv2.imread("图像完整路径")
kenrel=np.ones((3,3),np.unit8)
gradient=cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kenrel)
cv2.imshow('img',gradient)
(5)礼貌与黑貌
1)礼貌=原始输入-开运算结果。它还是调用morphologyEx方法,参数变成MORPH_TOPHAT即可。例如:
img=cv2.imread("图像完整路径")
kenrel=np.ones((3,3),np.unit8)
tophat=cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kenrel)
cv2.imshow('tophat',tophat)
2)黑貌=闭运算-原始输入。
它还是调用morphologyEx方法,参数变成MORPH_BLACKHAT即可。例如:
img=cv2.imread("图像完整路径")
kenrel=np.ones((3,3),np.unit8)
blackhat=cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kenrel)
cv2.imshow('blackhat',blackhat)
五.图像梯度计算
(1)sobel算子
1)在上面形状学中也是用卷积来算的,梯度也就是边缘信息(例如背景是黑色的,前景是白色的,白黑相交处的像素点信息),表示边缘像素点水平方向差异(卷积核右边减去左边的值<左列为负,右列为正>,例如用3行3列矩阵表示卷积核,设Gx是卷积核乘以像素点值的)与垂直方向差异(卷积核下边减去上边的值<下行为正,上行为正。通常是水平方向卷积核的转置矩阵>,例如用3行3列矩阵表示卷积核,设Gy是卷积核乘以像素点值的)得到的值。即Gx=某个卷积核矩阵*像素点组成的矩阵,Gy=某个卷积核矩阵*像素点组成的矩阵。算出Gx与Gy后一般是直接这个值相加就得到梯度值G或者(Gx的平方加上Gy的平方)和后开根得到梯度值G
2)这里是直接调用sobel方法。格式是sobel(src,ddepth,dx,dy,ksize),其中src是输入图像,ddepth是深度(通常设为-1),dx表示计算水平方向(用1表示算水平,用0表示不算水平的),dy表示计算竖直方向(用1表示算竖直的,用0表示不算竖直的),ksize是sobel算子大小(即卷积核大小)。例如:
img=cv2.imread("图像完整路径",cv2.IMREAD_GRAYSCALE)
sobel=cv2.sobel(img,cv2.CV_64F,1,0,ksize=3)
cv2.imshow('sobel',sobel)
像素边缘的白到黑是正数,黑到白是负数,opencv中所有负数都会被截断为0,所以这里要取绝对值来表示梯度值。例如如果背景是黑色的,前景是个白色的圆,那算水平方向的梯度后就会出现左边缘的白色信息,右边缘的信息全置为0,是黑色的。
(2)梯度计算方法
1)对上面黑色没显示出来的情况(默认负数截断为0了),解决方法就是取绝对值,这样算出来的梯度值都是大于等以0的数了。调用convertScalAbs方法就可得到绝对值。例如:
img=cv2.imread("图像完整路径",cv2.IMREAD_GRAYSCALE)
sobel=cv2.sobel(img,cv2.CV_64F,1,0,ksize=3)
Gx=cv2.convertScaleAbs(sobel)
cv2.imshow('sobel',Gx)。
2)算出Gx后,现在算Gy。
img=cv2.imread("图像完整路径",cv2.IMREAD_GRAYSCALE)
sobel=cv2.sobel(img,cv2.CV_64F,0,1,ksize=3)
Gy=cv2.convertScaleAbs(sobel)
cv2.imshow('sobel',Gy)。
3)现在调用addWeighted来求和得到梯度值G
G=vc2.addWeighted(Gx,0.5,Gy,0.5,0)
cv2.imshow('gradient',G)。
建议就用这三步来计算效果会好点,不要把1)与2)合并来计算。
(3)scharr算子与lapkacian算子
六.边缘检测
(1)canny边缘检测流程
流程:1)使用高斯滤波器,以平滑图像,滤除噪声。2)计算图像中每个像素点的梯度强度和方向(sobel算子) 3)利用非极大值抑制,以消除边缘检测带来的杂散响应。4)运用双阀值检测来确定真实和潜在的边缘。5)通过抑制孤立的弱边缘最终完成边缘检测。
(2)非极大值抑制
前面二步已经在上面说过,现在说下二种非极大值抑制。
1)线性插值法。
判断某个像素点梯度是否保留用线性非极大值法,即假设算出3*3矩阵中最中间点c梯度是否保留?已知算出边上四个点g1,g2,g3,g4的梯度值分别是mg1,mg2,mg3,mg4,那现在经c点画一条直线经过g1,g2之间和g3,g4之间,设与g1与g2相交点是q,与g3和g4相交点是z。那分别算出q与z的梯度出来,用mq与mz表示。mq=w*mg1+(1-w)*mg2,其中w=(g1到q点距离)/(g1到g2距离),mz也一样算出来。有mq与mz的梯度值后c=w*mq+(1-w)*mz,如果c比mq与mz都大,就保留c这个梯度值。
2)双阀值检测法
它有二个参数值maxval与minval,它分三种情况:如果某点的梯度值大于maxval就处理成边界(即保留);如果某点的梯度值小于minval就舍弃;如果minval<某点梯度值<maxval,这时某点若连接有边界就保留,否则也舍弃。maxval与minval设置得越小,检测到的边缘信息就会越丰富(但同时噪声点可能更多)。例如:
img=cv2.imread("图像完整路径",cv2.IMREAD_GRAYSCALE)
v1=cv2.Canny(img,100,200)
v2=cv2.Canny(img,80,150)
res=np.stack((v1,v2))
cv2.imshow('res',res)
七.图像金字塔与轮廓检测
(1)图像金字塔定义
在金字塔结构中,越靠近金字塔顶部的图像长宽大小会越小。这在以后的图像特征提取中不会只对原始图像进行特征提取,会对多层提取特征,不同层提取的特征值也许会不同,最后把提取出来的特征总结在一起。
1)高斯金字塔
<1>向下采样法(缩小):沿金字塔顶部方向走就是向下采样,即越来越小。
计算步骤:假设高斯内核是由5*5的矩阵组成,用A表示,图像数据用G表示,1>图像数据与高斯内核做内积并做归一化(即平均值),例如(1/16)*G*A。2>图像大小是不断缩小过程,例如原来是800*800的,下一层变成400*400,这例长宽缩小一半,面积就变成1/4了。所以这一步缩小的本质是偶数行与偶数列都去掉。
<2>向上采样法(放大):沿金字塔顶部反方向走就是向上采样,即沿这方向图像将越来越大。
计算步骤:1>将图像每个方向都扩大为原来的2倍(每个点行与列方向都扩大为2倍),新增的行与列用0填充。2>使用先前同样的内核(乘以4)与放大后的图像做卷积,获得近似值。
总结:不管是上采样(填充了0)还是下采样(去掉了行列),它与原始图像来说都是有损失的,所以说如果说先做下(或上)采样再做上(或下)采样是还原不回原来一模一样图像的。
2)拉普拉斯金字塔
计算公式为:L=G-PyrUp(PyrDown(G))
其中G刚开始是原始图像,以后就是每一层计算后的结果;PyrDown为下采样;PyrUp为上采样。貝体计算步骤:<1>低通滤波(相当于加卷积核)<2>缩小尺寸<3>放大尺寸<4>图像相减。其中前二步相当于下采样。
(2)金字塔制作方法
1)上采样
img=cv2.imread("图像完整路径")
print(img.shape)
up=cv2.pyrUp(img)
print(up.shape)
cv2.imshow('up',up)
2)下采样,调用pyrDown()方法。
(2)图像轮廓
1)以前梯度算出来的边缘不算轮廓,轮廓看上去是比较大块的。调用cv2.findContours(img,model,method),其中model与method分别会对应有好几个值的,img是指输入图像。其中model是指轮廓检测模式,model的值为RETR_TREE时最常用,RETR_TREE表示检索所有轮廓(类似把全部轮廓保存下来了,以后要用那个就直接调用那个即可),并重构嵌套轮廓的整个层次。method表示轮廓逼近方法,常用两个值分别是CHAIN_APPROX_NONE与CHAIN_APPROX_SIMPLE,其中CHAIN_APPROX_NONE是以freeman链码方式输出轮廓,所有其他方法输出多边形(顶点的序列),例如长方形的图像就会得到长与宽的轮廓;其中CHAIN_APPROX_SIMPLE表示压缩水平,垂直,斜的部分(即只保留他们的终点部分),例如长方形输出的轮廓只会保留4个顶点。
2)处理步骤:<1>读取得到图像img <2>调用cvtColour转为灰度图<3>调用threshold把灰度图转为二值图,这里做二值图是为了更好地做边缘检测<4>有了二值后,这时才能用findContours方法,二值就是输入图像。
(3)图像轮廓检测结果
1)假设执行前三步得到的二值是x,现在调第四步:b,c,h=cv2.findContours(x,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
print(c.shape)
这里的c就是轮廓值
2)有了轮廓值然后就绘制轮廓。调用drawContours方法来绘制,这个方法有五个参数drawContours(draw_img,c,d,e,f),其中draw_img是指绘制的图像,c是指轮廓,d是指轮廓索引,d=-1就表示所有轮廓都画出来,e是指颜色模式,用bgr格式表示,f是指线条厚度。注意绘制前要先把原始图像复制一份作为参数传入,例如:
img_copy=img.copy()
res=cv2.drawContours(img_copy,c,-1,(0,0,255),2)
cv2.imshow('res',res)
(4)轮廓特征与近似
1)对轮廓进行计算时,需先把那一个轮廓取出来,例如接上面的:lk=c[0]
,其中c是上面得到的轮廓
<1>计算面积。调用contourArea方法,例如
area=cv2.contourArea(lk)
print(area)
<2>计算周长。调用arcLength方法,例如
arc=cv2.arcLength(lk,true)
print(arc)
2)轮廓近似
<1>原理:一条ab画出来的类似半圆曲线,ab就想像成半圆直径,曲线上最远点c垂直于直径构成cd为垂直长度,假设阀值为e,如果cd<e,就用ab直径代替ab曲线(近似表示),否则就画出ac与bc这个直角三角形的其它二边,现以这二条边为底,也分别从某点开始画出直角,重复上面的计算方式,最终得出轮廓近似。
<2>计算方式
1>先得到貝体那个轮廓,然后调用approxPolyDP(lk,f,bool)得到轮廓近似,其中lk是指具体的轮廓;f表示指定的阀值,通常用周长的百分比值;bool通常用true表示。例如
f=0.1*(cv2.arcLength(lk,true))
其中f是指周长的0.1倍
approx=cv2.approxPolyDP(lk,f,true)
下面绘制轮廓近似:
img_copy=img.copy()
res=cv2.drawContours(img_copy,[approx],-1,(0,0,255),2)
cv2.imshow('res',res)
轮廓近似还可通过外接矩阵与外接圆来表示。
(5)模板匹配方法
一张模板图像(类似卷积核,把它看作3*3的图像)在原始图像中那一部分最匹配,类似卷积核在原始图像中进行从左到右,从上到下进行匹配,找出最相似的部分。
八.直方图与傅里叶变换
(1)直方图定义
直方图中用x轴从0到255的值表示每个像素点(共256个),y轴表示每个像素点的个数。调用方法是calcHist(img,channel,mask,hisSize,ranges)
其中img表示输入图像,用[]号括起来;channel表示统计的颜色通道,如果是灰度图用[0]表示,如果是彩色图就用[0][1][2]表示BGR;mask表示掩模图像,若对整张图像做直方统计就用None表示,否则就设定好大小;hisSize:bin数目,也用中括号[]括起来;ranges:像素值范围,常用[0,256]表示。
例如:
img=cv2.imread("图像完整路径",0)
这里后面加0表示灰度图,如果是彩色图可不加这个参数,彩色图是包括BGR三个颜色通道的。
print(img.shape)
up=cv2.calcHist([img],[0],None,[256],[0,256])
print(up.shape)
cv2.imshow('up',up)
(2)均衡化处理
1)均衡化原理是从原来图像经映射后得到新的像素值。那它是怎样映射的呢?
<1>先算出每个原始像素点所占比例(概率),例如像素值50,128,200,255的概率分别是0.25,0.1875,0.3125,0.25
<2>然后由<1>算出它们的累积概率值分别是0.25,0.4375,0.75,1
<3>映射方法是:累积概率值*(255-0),所以分别计算出映射后的新灰度值是63.75,111.56,191.25,255
<4>对新灰度值四舍五入取整为64,112,191,255
(3)均衡化效果
调用equalizeHist()方法来达到均衡化,例如:
img=cv2.imread("图像完整路径",0)
up=cv2.equalizeHist(img)
调用plt来画图:
plt.hist(up.ravel(),256)
plt.show()
print(up.shape)
cv2.imshow('up',up)
与原始图像连接一起对比看:
res=np.hstack((img,up))
cv2.imshow('res',res)
对比会发现均衡化后的图像更亮一些,细节有可能看得更不清晰,感觉会丟失一些信息一样。
(4)傅里叶概述
1)以时间为参照的就是时域分析,例上午8点吃饭,10点打篮球,12点回家。以频域分析的是傅里叶。
2)傅里叶变换的作用
高频:变化剧烈的灰度分量,例如边界。
低频:变化缓慢的灰度分量,例如一片大海。
低通(低频)滤波:只保留低频的,会使图像模糊。
高通(高频)滤波:只保留高频的,会使图像细节增强。
3)用法
<1>傅里叶变换中,opencv主要调用cv2.dft与cv2.idft(这二个是逆运算),其中idft是从傅里叶变换结果中变回原始图像,但dft的输入图像需先转成np.float32格式。
<2>得到结果中频率为0的会在左上角,通常转到中心位置,可以通过shift变换来实现。
<3>cv2.dft返回的结果是双通道(实部与虚部)的,通常还需要转换成图像格式才能显示(0,255)。
十一.图像特征-harries角点检测
(1)图像特征-harries概述
平面:一张区域图像沿x与y轴移动时都是比较平稳,变化不是很大。在图像中占比比较大。即两个特征值都比较小,且近似相等。
边界:一张区域图像沿x轴或y轴某一个方向移动时发生变化明显,另一方向不是很明显。即一个特征值比较大,另一特征值较小。
角点:一张区域图像沿x轴和y轴方向移动时都会发生变化明显,特征比较明显。特征变化讯速,可快速定位到。即两个特征值都比较大,且近似相等。
(2)基本数学原理
(3)求解化简
(4)特征归属划分
二次项函数本质上就是一个椭圆函数。
(5)opencv角点检测效果
cv2.cornerHarries(img,blocksize,ksize,k):其中img表示输入为float32的输入图像;blocksize表示角点检测中指定区域的大小;ksize表示sobel求导中使用的窗口大小;k表示取值参数为[0.04,0.06]范围,通常取0.04
十二.图像特征-sift
(1)尺度空间定义
例如用400*400,200*200,100*100这些尺寸去提特征
(2)高斯差分金字塔(DOG)
相同尺寸(例400*400或200*200等等)中相邻滑动出的关键点就是相邻窗口提取出的特征值差最大(极值点)的,这个就认为是比较有价值的特征点,可转成相应的特征向量。
(3)特征关键点定位
找极值点是指某一点不光要与周围点比较大小,还要与相邻上下二层比较,例如窗口大小是3*3的,那某一点就要与26个点比较了。
对于得到的离散型的多个极值(例曲线上的多个离散点)可利用泰勒级数展开(二阶导够了)得到近似的关键点(真正的极值点,找到连续上的真正的极值)。矩阵求导过程中加上转置。
(4)生成特征描述
找到极值点后,要想计算机认识那需把它转成向量。每个点都有它的梯度模值与方向。可用8个方向(360度每隔45度就一个方向)统计出它的直方图(每个方向的数值)就行了,数值最大的一个方向就是特征主方向。
(5)特征向量生成
找到关键点(真正的极值点)后,论文中说每个关键点用窗口大小为4*4共16个种子描述,因为每个小窗口都是有8个方向的值(8维向量),所以一个关键点一共就有16*8=128维的sift特征向量,有向量就有相应的特征值了。
(6)opencv中sift方法的使用
十七.光流估计