1、图像梯度
x,y方向上的梯度
# 图像梯度(由x,y方向上的偏导数和偏移构成),有一阶导数(sobel算子)和二阶导数(Laplace算子)
# 用于求解图像边缘,一阶的极大值,二阶的零点
# 一阶偏导在图像中为一阶差分,再变成算子(即权值)与图像像素值乘积相加,二阶同理
def sobel_demo(iamge): grad_x=cv.Sobel(iamge,cv.CV_32F,1,0) grad_y=cv.Sobel(iamge,cv.CV_32F,0,1) gradx=cv.convertScaleAbs(grad_x) grady=cv.convertScaleAbs(grad_y) cv.imshow("gradient_x",gradx) cv.imshow("gradient_y",grady)
xy方向相加得最终结果
gradxy=cv.addWeighted(gradx,0.5,grady,0.5,0)
选用更有效的 Scharr算子
lapalian算子得出的结果
def lapalian_demo(image): dst=cv.Laplacian(image,cv.CV_32F) lpls=cv.convertScaleAbs(dst) cv.imshow("lapalian_demo",lpls)
自定义lapalian算子,八领域
def custom_lapalian(image): kernel=np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]]) dst=cv.filter2D(image,cv.CV_32F,kernel=kernel) grady=cv.convertScaleAbs(dst) cv.imshow("lapalian_custom",grady)
2、Canny边缘提取
# canny运算步骤:5步
# 1. 高斯模糊 - GaussianBlur 去噪声
# 2. 灰度转换 - cvtColor 多通道变成单通道
# 3. 计算梯度 - Sobel/Scharr
# 4. 非极大值抑制 进行高值的过滤 t1 t2高低阈值
# 5. 高低阈值输出二值图像
# 非极大值抑制:
# 算法使用一个3×3邻域作用在幅值阵列M[i,j]的所有点上;
# 每一个点上,邻域的中心像素M[i,j]与沿着梯度线的两个元素进行比较,
# 其中梯度线是由邻域的中心点处的扇区值ζ[i,j]给出。
# 如果在邻域中心点处的幅值M[i,j]不比梯度线方向上的两个相邻点幅值大,则M[i,j]赋值为零,否则维持原值;
# 此过程可以把M[i,j]宽屋脊带细化成只有一个像素点宽,即保留屋脊的高度值。
# 高低阈值连接
# T1,T2为阈值,凡是高于T2的都保留,凡是低于T1的都丢弃
# 从高于T2的像素出发,凡是大于T1而且相互连接的都保留。最终得到一个输出二值图像
# 推荐高低阈值比值为T2:T1 = 3:1/2:1,其中T2高阈值,T1低阈值
Canny边缘检测 彩色和黑白
def edge_demo(image): blurred=cv.GaussianBlur(image,(3,3),0) gray=cv.cvtColor(blurred,cv.COLOR_BGR2GRAY) grad_x=cv.Sobel(gray,cv.CV_16SC1,1,0) grad_y=cv.Sobel(gray,cv.CV_16SC1,0,1) edge_output=cv.Canny(grad_x,grad_y,50,150) cv.imshow("gray",gray) cv.imshow("Canny demo",edge_output) dst=cv.bitwise_and(image,image,mask=edge_output) cv.imshow("color edge",dst)
edge_output=cv.Canny(gray,50,150)直接调用也可以
3、直线检测,霍夫直线变换
另一方面,也可以写成关于(k,q)的函数表达式(霍夫空间):
对应的变换可以通过图形直观表示:
变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。
第二种,直接给定x1x2等
def line_detect_possible_demo(image): gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) edges = cv.Canny(gray, 50, 150, apertureSize=3) lines = cv.HoughLinesP(edges, 1, np.pi / 180, 200,minLineLength=50,maxLineGap=10) for line in lines: print(type(line)) x1,y1,x2,y2=line[0] cv.line(image,(x1,y1),(x2,y2),(0,0,255),2) cv.imshow("line_detect_possible_demo",image)
圆检测:
还不熟悉参数的具体用法,调了很多次要不就是少了要补多了
后来发现,霍夫圆检测是对噪声敏感的算法,所以将均值迁移的属性值更改,最终达到目的
dst=cv.pyrMeanShiftFiltering(image,15,100)
# 均值迁移,sp,sr为空间域核与像素范围域核半径
def detection_circles_demo(image): dst=cv.pyrMeanShiftFiltering(image,10,100) # 均值迁移,sp,sr为空间域核与像素范围域核半径 gray=cv.cvtColor(dst,cv.COLOR_BGR2GRAY) circles=cv.HoughCircles(gray,cv.HOUGH_GRADIENT,1,20,param1=40,param2=27,minRadius=0,maxRadius=0) """ . @param image 8-bit, single-channel, grayscale input image. . @param circles Output vector of found circles. Each vector is encoded as 3 or 4 element . floating-point vector \f$(x, y, radius)\f$ or \f$(x, y, radius, votes)\f$ . . @param method Detection method, see #HoughModes. Currently, the only implemented method is #HOUGH_GRADIENT . @param dp Inverse ratio of the accumulator resolution to the image resolution. For example, if . dp=1 , the accumulator has the same resolution as the input image. If dp=2 , the accumulator has . half as big width and height. 累加器图像的分辨率。这个参数允许创建一个比输入图像分辨率低的累加器。 (这样做是因为有理由认为图像中存在的圆会自然降低到与图像宽高相同数量的范畴)。 如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。 dp的值不能比1小。 . @param minDist Minimum distance between the centers of the detected circles. If the parameter is . too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is . too large, some circles may be missed. 该参数是让算法能明显区分的两个不同圆之间的最小距离。 . @param param1 First method-specific parameter. In case of #HOUGH_GRADIENT , it is the higher . threshold of the two passed to the Canny edge detector (the lower one is twice smaller). 用于Canny的边缘阀值上限,下限被置为上限的一半。 . @param param2 Second method-specific parameter. In case of #HOUGH_GRADIENT , it is the . accumulator threshold for the circle centers at the detection stage. The smaller it is, the more . false circles may be detected. Circles, corresponding to the larger accumulator values, will be . returned first. 累加器的阀值。 . @param minRadius Minimum circle radius. 最小圆半径 . @param maxRadius Maximum circle radius. If <= 0, uses the maximum image dimension. If < 0, returns . centers without finding the radius. 最大圆半径。 """ circles=np.uint16(np.around(circles)) print(circles.shape) for i in circles[0,:]: cv.circle(image,(i[0],i[1]),i[2],(0,255,0),2) cv.circle(image,(i[0],i[1]),2,(0,0,255),3) cv.imshow("detection_circles",image)
4、轮廓发现
""" . 轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同 的颜色或者灰度。 轮廓在形状分析和物体的检测和识别中很有用。 . 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者 Canny 边界检测 . 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图像的话, 你应该将原始图像存储到其他变量中. . 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。要找的物体应该是白色而背景应该是黑色。 """ def contours_demo(image): dst=cv.GaussianBlur(image,(3,3),0) gray=cv.cvtColor(dst,cv.COLOR_BGR2GRAY) ret,binary=cv.threshold(gray,0,255,cv.THRESH_OTSU|cv.THRESH_BINARY) cv.imshow("binary image",binary) contours,heriachy=cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) """ ? 函数 cv2.?ndContours() 有三个参数, 第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。 ? 检索模式: ? CV_RETR_EXTERNAL - 只提取外层的轮廓 ? CV_RETR_LIST - 提取所有轮廓,并且放置在 list 中 ? CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 顶层为连通域的 外围边界,次层为洞的内层边界。 ? CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy ? 逼近方法 (对所有节点, 不包括使用内部逼近的 CV_RETR_RUNS). 点的存贮情况,是不是都被存贮 ? CV_CHAIN_CODE - Freeman 链码的输出轮廓. 其它方法输出多边形(定点序列). ? CV_CHAIN_APPROX_NONE - 将所有点由链码形式翻译为点序列形式 ? CV_CHAIN_APPROX_SIMPLE - 压缩水平、垂直和对角分割,即函数只保留末端的象素 点; ? CV_CHAIN_APPROX_TC89_L1, CV_CHAIN_APPROX_TC89_KCOS - 应用 Teh-Chin 链逼近算法. ? CV_LINK_RUNS - 通过连接为 1 的水平碎片使用完全不同的轮廓提取算法。仅有 CV_RETR_LIST 提取模式可以在本方法中应用. ? offset:每一个轮廓点的偏移量. 当轮廓是从图像 ROI 中提取出来的时候,使用偏移量有用,因为可以从整个图像上下文来对轮廓做分析. ? 返回值有三个,第一个是轮廓,个是图像,第二第三个是(轮廓的)层析结构。 轮廓(第二个返回值)是一个 Python 列表,其中存储这图像中的所有轮廓。 每一个轮廓都是一个 Numpy 数组,包含对象边界点(x,y)的坐标。 """ for i,contour in enumerate(contours): # 函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。 # 它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。 # 第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设 置为 -1 时绘制所有轮廓)。 # 接下来的参数是轮廓的颜色和厚度等。 cv.drawContours(image,contours,i,(0,0,255),-1) print(i) cv.imshow("detect contours",image)
利用canny检测
def edge_demo(image): blurred = cv.GaussianBlur(image, (3, 3), 0) gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY) grad_x = cv.Sobel(gray, cv.CV_16SC1, 1, 0) grad_y = cv.Sobel(gray, cv.CV_16SC1, 0, 1) # edge_output = cv.Canny(grad_x, grad_y, 30, 150) edge_output = cv.Canny(gray, 50, 150) return edge_output
5、对象测量
def measure_demo(image): gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY) ret,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY_INV|cv.THRESH_OTSU) print("threshold value:%s"%ret) cv.imshow("binary image",binary) contours,hierarchy=cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE) for i,contour in enumerate(contours): area=cv.contourArea(contour) x,y,w,h=cv.boundingRect(contour) mm=cv.moments(contour)# 函数 cv2.moments() 会将计算得到的矩以一个字典的形式返回 # 计算出对象的重心 type(mm) if mm[‘m00‘]==0: continue else: cx = mm[‘m10‘] / mm[‘m00‘] cy = mm[‘m01‘] / mm[‘m00‘] cv.circle(image,(np.int(cx),np.int(cy)),2,(0,255,255),3) cv.rectangle(image,(x,y),(x+w,y+h),(0,0,255),2) cv.imshow("measure_contours",image)
6、腐蚀膨胀
def erode_demo(image): print(image.shape) gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY) ret ,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) cv.imshow("binary",binary) kernel=cv.getStructuringElement(cv.MORPH_RECT,(5,5)) dst=cv.erode(binary,kernel) cv.imshow("erode_demo",dst) def dilate_demo(image): print(image.shape) gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY) ret ,binary=cv.threshold(gray,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU) cv.imshow("binary",binary) kernel=cv.getStructuringElement(cv.MORPH_RECT,(5,5)) dst=cv.dilate(binary,kernel) cv.imshow("dilate_demo",dst)
就像土壤侵蚀一样,这个操作会把前景物体的边界腐蚀掉(但是前景仍然 是白色)。
卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是 1,
那么中心元素就保持原来的像素值,否则就变为零。
这回产生什么影响呢?根据卷积核的大小靠近前景的所有像素都会被腐蚀掉(变为 0),
所以前景物体会变小,整幅图像的白色区域会减少。
这对于去除白噪声很有用,也可以用来断开两个连在一块的物体等。
与腐蚀相反,与卷积核对应的原图像的像素值中只要有一个是 1,中心元 素的像素值就是 1。
所以这个操作会增加图像中的白色区域(前景)。一般在去 噪声时先用腐蚀再用膨胀。
因为腐蚀在去掉白噪声的同时,也会使前景对象变 小。所以我们再对他进行膨胀。
这时噪声已经被去除了,不会再回来了,但是 前景还在并会增加。
膨胀也可以用来连接两个分开的物体。
原文:https://www.cnblogs.com/jiakun/p/14135582.html