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方法的使用
十七.光流估计
相关文章:
opencv图像处理框架
一.课程简介与环境配置 二.图像基本操作 (1)计算机眼中的视觉 1)计算机眼中图像是由一块块组成,每一块又由很多很多个像素点组成,一个像素点的值是在0到255之间,值越大就越亮。 2)RGB表示彩色图像的三个颜色通道(红绿蓝),一张…...
MotionLCM 部署笔记
目录 依赖项 humanml3d: sentence-t5-large 下载数据: 报错:No module named sentence_transformers 继续报错:from transformers.integrations import CodeCarbonCallback 解决方法: 推理相关 GitHub - Dai-W…...
BUUCTF_[安洵杯 2019]easy_web(preg_match绕过/MD5强碰撞绕过/代码审计)
打开靶场,出现下面的静态html页面,也没有找到什么有价值的信息。 查看页面源代码 在url里发现了img传参还有cmd 求img参数 这里先从img传参入手,这里我发现img传参好像是base64的样子 进行解码,解码之后还像是base64的样子再次进…...
LLM - 基于LM Studio本地部署DeepSeek-R1的蒸馏量化模型
文章目录 前言开发环境快速开始LM Studio简单设置模型下载开始对话 模型选择常见错误最后 前言 目前,受限于设备性能,在本地部署的基本都是DeepSeek-R1的蒸馏量化模型,这些蒸馏量化模型的表现可能并没有你想象的那么好。绝大部分人并不需要本…...
Intel 与 Yocto 项目的深度融合:全面解析与平台对比
在嵌入式 Linux 领域,Yocto 项目已成为构建定制化 Linux 发行版的事实标准,广泛应用于不同架构的 SoC 平台。Intel 作为 x86 架构的领导者,在 Yocto 生态中投入了大量资源,为其嵌入式处理器、FPGA 和 AI 加速硬件提供了完整的支持…...
2025-工具集合整理
科技趋势 github-rank 🕷️Github China/Global User Ranking, Global Warehouse Star Ranking (Github Action is automatically updated daily). 科技爱好者周刊 制图工具 D2 D2 A modern diagram scripting language that turns text to diagrams 文档帮助 …...
快速提升网站收录:利用网站新闻发布功能
本文转自:百万收录网 原文链接:https://www.baiwanshoulu.com/63.html 利用网站新闻发布功能快速提升网站收录是一个有效的策略。以下是一些具体的建议,帮助你更好地利用这一功能: 一、保持新闻更新频率 搜索引擎尤其重视网站的…...
wxss样式模板,全局配置window
1 wxss样式模板 1.1什么是wxss 1.2 rpx 1.3 样式导入 1.4 全局样式 1.5局部样式 2 全局配置 2.1 全局配置window 2.2 window 导航栏区域...
git多人协作
目录 一、项目克隆 二、 1、进入克隆仓库设置 2、协作处理 3、冲突处理 4、多人协作分支的推送拉取删除 1、分支推送(2种) 2、远程分支拉取(2种) 3、远程分支删除 一、项目克隆 git clone 画船听雨眠/test1 (自定义的名…...
Maven全解析:从基础到精通的实战指南
概念: Maven 是跨平台的项目管理工具。主要服务基于 Java 平台的构建,依赖管理和项目信息管理项目构建:高度自动化,跨平台,可重用的组件,标准化的流程 依赖管理: 对第三方依赖包的管理…...
使用Pytorch训练一个图像分类器
一、准备数据集 一般来说,当你不得不与图像、文本或者视频资料打交道时,会选择使用python的标准库将原始数据加载转化成numpy数组,甚至可以继续转换成torch.*Tensor。 对图片而言,可以使用Pillow库和OpenCV库对视频而言…...
除了成本核算,还有哪些财务分析工具可以提高工作效率?
除了成本核算,财务工作中还有多种分析工具可以提高工作效率,以下是详细介绍: 一、数据可视化工具 Power BI:这是一款强大的数据可视化工具,通过创建交互式报表、仪表板和图表来展示财务数据。它易于使用,提…...
【SSM】Spring + SpringMVC + Mybatis
SSM课程,以下为该课程的笔记 bean:IOC容器创建的对象 P12 bean的生命周期 在bean中定义init()和destroy()方法,然后在xml中配置方法名,让bean对象能找到对应的生命周期方法。 或通过实现接口的方式定义声明周期方法。 P13 sett…...
Windows图形界面(GUI)-QT-C/C++ - QT Tab Widget
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 一、概述 1.1 什么是 QTabWidget? 1.2 使用场景 二、常见样式 2.1 选项卡式界面 2.2 动态添加和删除选项卡 2.3 自定义选项卡标题和图标 三、属性设置 3.1 添加页面&…...
pstricks PGFTikz 在CTeX套装中绘图Transparency或Opacity失效的问题
我在CTeX中画图的时候,习惯用Geogebra先画好,然后生成pstricks或PGFTikz代码: 这样不用插入eps或pdf之类的图片,也是一种偷懒的方法。以前往arXiv.org上面传论文也是这样:代码出图,就不用另外上传一幅eps或…...
操作系统和中间件的信息收集
在浏览器中收集操作系统与中间件信息时,主要通过客户端JavaScript(用于操作系统/浏览器信息)和服务器端脚本(用于中间件信息)实现。以下是分步指南: 一、客户端操作系统信息收集(JavaScript&am…...
Android --- handler详解
handler 理解 handler 是一套Android 消息传递机制,主要用于线程间通信。 tips: binder/socket 用于进程间通信。 参考: Android 进程间通信-CSDN博客 handler 就是主线程在起了一个子线程,子线程运行并生成message ,l…...
C++:结构体和类
在之前的博客中已经讲过了C语言中的结构体概念了,重复的内容在这儿就不赘述了。C中的结构体在C语言的基础上还有些补充,在这里说明一下,顺便简单地讲一下类的概念。 一、成员函数 结构体类型声明的关键字是 struct ,在C中结构体…...
初级数据结构:栈和队列
目录 一、栈 (一)、栈的定义 (二)、栈的功能 (三)、栈的实现 1.栈的初始化 2.动态扩容 3.压栈操作 4.出栈操作 5.获取栈顶元素 6.获取栈顶元素的有效个数 7.检查栈是否为空 8.栈的销毁 9.完整代码 二、队列 (一)、队列的定义 (二)、队列的功能 (三)…...
携程Java开发面试题及参考答案 (200道-下)
insert 一行数据的时候加的是什么锁?为什么? 在 MySQL 中,当执行 INSERT 操作插入一行数据时,加锁的情况会因存储引擎和具体的事务隔离级别而有所不同。一般来说,在 InnoDB 存储引擎下,INSERT 操作加的是行级排他锁(Row Exclusive Lock),以下详细说明原因。 行级排他…...
Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...
98,【6】 buuctf web [ISITDTU 2019]EasyPHP
进入靶场 代码 <?php // 高亮显示当前 PHP 文件的源代码,通常用于调试或展示代码,方便用户查看代码逻辑 highlight_file(__FILE__);// 从 GET 请求中获取名为 _ 的参数值,并赋值给变量 $_ // 符号用于抑制可能出现的错误信息ÿ…...
54【ip+端口+根目录通信】
上节课讲到,根目录起到定位作用,比如我们搭建一个php网站后,注册系统是由根目录的register.php文件执行,那么我们给这个根目录绑定域名https://127.0.0.1,当我们浏览器访问https://127.0.0.1/register.php时࿰…...
计算机网络 应用层 笔记 (电子邮件系统,SMTP,POP3,MIME,IMAP,万维网,HTTP,html)
电子邮件系统: SMTP协议 基本概念 工作原理 连接建立: 命令交互 客户端发送命令: 服务器响应: 邮件传输: 连接关闭: 主要命令 邮件发送流程 SMTP的缺点: MIME: POP3协议 基本概念…...
解析PHP文件路径相关常量
PHP文件路径相关常量包括以下几个常量: __FILE__:表示当前文件的绝对路径,包括文件名。 __DIR__:表示当前文件所在的目录的绝对路径,不包括文件名。 dirname(__FILE__):等同于__DIR__,表示当前…...
蓝桥与力扣刷题(234 回文链表)
题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head [1,2,2,1] 输出:true示例 2: 输入&…...
【协议详解】卫星通信5G IoT NTN SIB33-NB 信令详解
一、SIB33信令概述 在5G非地面网络(NTN)中,卫星的高速移动性和广域覆盖特性使得地面设备(UE)需要频繁切换卫星以维持连接。SIB32提供了UE预测当前服务的卫星覆盖信息,SystemInformationBlockType33&#x…...
c语言练习题【数据类型、递归、双向链表快速排序】
练习1:数据类型 请写出以下几个数据的数据类型 整数 a a 的地址 存放a的数组 b 存放a的地址的数组 b的地址 c的地址 指向 printf 函数的指针 d 存放 d的数组 整数 a 的类型 数据类型是 int a 的地址 数据类型是 int*(指向 int 类型的指针) …...
SliverAppBar的功能和用法
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容,本章回中将介绍SliverAppBar组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似,它们的…...
deepseek 本地化部署和小模型微调
安装ollama 因为本人gpu卡的机器系统是centos 7, 直接使用ollama会报 所以ollama使用镜像方式进行部署, 拉取镜像ollama/ollama 启动命令 docker run -d --privileged -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 查看ollama 是否启动…...
Heptagon 同步语言介绍
同步语言于20世纪80年代创立,用于建模、设计和实现实时关键反应系统。随着被控制系统的复杂性不断增加,执行速度成为一个重要标准。与此同时,处理器在核心数量上的增长超过了速度的提升。因此,我们正在寻求一种并行执行方式&#…...
Golang 并发机制-5:详解syn包同步原语
并发性是现代软件开发的一个基本方面,Go(也称为Golang)为并发编程提供了一组健壮的工具。Go语言中用于管理并发性的重要包之一是“sync”包。在本文中,我们将概述“sync”包,并深入研究其最重要的同步原语之一…...
数组排序算法
数组排序算法 用C语言实现的数组排序算法。 排序算法平均时间复杂度最坏时间复杂度最好时间复杂度空间复杂度是否稳定适用场景QuickO(n log n)O(n)O(n log n)O(log n)不稳定大规模数据,通用排序BubbleO(n)O(n)O(n)O(1)稳定小规模数据,教学用途InsertO(n)…...
利用腾讯云cloud studio云端免费部署deepseek-R1
1. cloud studio 1.1 cloud studio介绍 Cloud Studio(云端 IDE)是基于浏览器的集成式开发环境,为开发者提供了一个稳定的云端工作站。支持CPU与GPU的访问。用户在使用 Cloud Studio 时无需安装,随时随地打开浏览器即可使用。Clo…...
Codeforces Round 1002 (Div. 2)(A-D)
题目链接:Dashboard - Codeforces Round 1002 (Div. 2) - Codeforces A. Milya and Two Arrays 思路 数组a中不同数的数量*数组b的,就是能够组成不同数的数量 代码 void solve(){int n;cin>>n;int cnt10;int cnt20;map<int,bool> mp;ma…...
半导体器件与物理篇7 微波二极管、量子效应和热电子器件
基本微波技术 微波频率:微波频率涵盖约从0.1GHz到3000GHz,相当于波长从300cm到0.01cm。 分布效应:电子部件在微波频率,与其在较低频率的工作行为不同。 输运线:一个由电阻、电容、电感三种等效基本电路部件所组成的…...
Hot100之图论
200岛屿数量 题目 思路解析 把访问过的格子插上棋子 思想是先污染再治理,我们有一个inArea()函数,是判断是否出界了 我们先dfs()放各个方向遍历,然后我们再把这个位置标为0 我们岛屿是连着…...
CSS 样式化表格:从基础到高级技巧
CSS 样式化表格:从基础到高级技巧 1. 典型的 HTML 表格结构2. 为表格添加样式2.1 间距和布局2.2 简单的排版2.3 图形和颜色2.4 斑马条纹2.5 样式化标题 3. 完整的示例代码4. 总结 在网页设计中,表格是展示数据的常见方式。然而,默认的表格样式…...
DeepSeek相关技术整理
相关介绍 2024年12月26日,DeepSeek V3模型发布(用更低的训练成本,训练出更好的效果)671B参数,激活37B。2025年1月20日,DeepSeek-R1模型发布(仅需少量标注数据(高质量长cotÿ…...
Spring Boot框架下的单元测试
1. 什么是单元测试 1.1 基本定义 单元测试(Unit Test) 是对软件开发中最小可测单位(例如一个方法或者一个类)进行验证的一种测试方式。在 Java 后端的 Spring Boot 项目中,单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快…...
OpenAI 实战进阶教程 - 第四节: 结合 Web 服务:构建 Flask API 网关
目标 学习将 OpenAI 接入 Web 应用,构建交互式 API 网关理解 Flask 框架的基本用法实现 GPT 模型的 API 集成并返回结果 内容与实操 一、环境准备 安装必要依赖: 打开终端或命令行,执行以下命令安装 Flask 和 OpenAI SDK: pip i…...
xss-labs靶场
xss-labs靶场 xss攻击类型 反射型xss 即攻击者将恶意脚本嵌入到url或者表单中,当用户访问特定的url或者提交表单时(用户端请求时),恶意脚本会执行 攻击需要用户点击恶意链接或访问包含恶意参数的url触发 存储型xss 即攻击者将恶意脚本提交…...
Eigen::Tensor使用帮助
0 引言 用python实现了某些算法之后,想转成C来获取更高的性能。但是python数组的操作太灵活了,尤其是3维、4维、5维等高维数组,以及它们的广播、数组坐标、切片等机制。还有numpy的pad、where等操作更是给C转换带来了更多的麻烦。 查阅了相…...
高阶开发基础——快速入门C++并发编程4
目录 使用call_once来确保调用的唯一性 先看我们的原始的单例模式 使用call_once来确保调用的唯一性 一个相似的概念是——单例模式,笔者找到的是stack_overflow的一个问答,如果不喜欢看英文,可以考虑看一下这个CSDN回答: c - H…...
C++基础day1
前言:谢谢阿秀,指路阿秀的学习笔记 一、基础语法 1.构造和析构: 类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。 构造顺序:父类->子类 析…...
Deepseek:网页版OR本地部署版本?
使用本地部署的 DeepSeek 还是网页版的 DeepSeek,取决于具体需求和使用场景。以下是两者的对比及推荐建议: 响应速度 网页版 DeepSeek:响应速度受网络状况和服务器负载影响较大。如果网络不稳定或服务器繁忙,可能会出现延迟甚至…...
【文件上传】
目录 一. 介绍二. 本地存储三. 阿里云OSS3.1 准备工作3.2 入门程序3.3 案例集成3.4 程序优化 \quad 一. 介绍 \quad 三要素缺一不可 \quad 二. 本地存储 \quad 解决相同命名覆盖问题 \quad 三. 阿里云OSS \quad \quad 3.1 准备工作 \quad \quad 3.2 入门程序 \quad \quad 3.3…...
股票入门知识
股票入门(更适合中国宝宝体制) 股市基础知识 本文介绍了股票的基础知识,股票的分类,各板块发行上市条件,股票代码,交易时间,交易规则,炒股术语,影响股价的因素…...
Debezium Oracle Connector SCN处理优化指南
Debezium Oracle Connector SCN处理优化指南 📌 问题场景 SCN跳跃场景: 起始SCN:15,000(含数据变更)结束SCN:1,000,000(无中间数据)默认批次大小:10,000 → 需执行985次无效查询🚀 优化方案 1. 自适应批次调整 代码位置:LogMinerStreamingChangeEventSource.j…...
2021版小程序开发5——小程序项目开发实践(1)
2021版小程序开发5——小程序项目开发实践(1) 学习笔记 2025 使用uni-app开发一个电商项目; Hbuidler 首选uni-app官方推荐工具:https://www.dcloud.io/hbuilderx.htmlhttps://dev.dcloud.net.cn/pages/app/list 微信小程序 管理后台:htt…...