opencv3处理图像

    xiaoxiao2023-11-02  143

    1.傅里叶变换 为什么要傅里叶变换? 傅里叶变换可以将人眼看到的不同频率的波形分离出来,可以区分出哪些区域的信号(图像像素)变化特别强,哪些区域的信号变化不那么强,从而可以任意的标记噪声区域,感兴趣区域,前景和背景等。

    傅里叶变换是很多常见的图形处理操作的基础,比如边缘检测,形状线段的检测。

    高通滤波器: 通俗来说,就是取小块图片,计算中央像素与周围邻近像素的亮度差值之后,如果亮度变化很大,那么中央像素的亮度就会增加,反之不会,变化很大的地方,就让他亮度更大。 低通滤波器:与高通滤波器相反,如果在某处像素与周围邻近像素的亮度差值很小,小于某一特定值,就平滑该像素的亮度,使其更不突出。主要用于去噪和模糊化。(高斯模糊是最常见的模糊滤波器,削弱高信号强度的低通滤波器)

    ###FFT变换 import cv2 import numpy as np from scipy import ndimage import matplotlib.pyplot as plt kernel_3_3 = np.array([[-1,-1,-1], [-1,8,-1], [-1,-1,-1]]) kernel_5_5 = np.array([[-1,-1,-1,-1,-1], [-1,1,2,1,-1], [-1,2,4,2,-1], [-1,1,2,1,-1], [-1,-1,-1,-1,-1]]) #这些滤波器中各数值之和是0

    下面用这两个高通滤波器做实验 随便选一副图像进行测试:

    img = cv2.imread('E:\\wms1.jpg',0) #cv2.imshow('result',img) k3 = ndimage.convolve(img,kernel_3_3) k5 = ndimage.convolve(img,kernel_5_5) blurred = cv2.GaussianBlur(img,(11,11),0) g_hpf = img - blurred cv2.imshow('3*3',k3) cv2.imshow('5*5',k5) cv2.imshow('g_hpf',g_hpf) cv2.waitKey() cv2.destroyAllWindows()

    分别用kernel_3_3和kernel_5_5和原图减去高斯低通得到的高通来处理原图。

    cv2.imshow('original',img) cv2.imshow('3*3',k3) cv2.imshow('5*5',k5) cv2.imshow('ditonglvbo',blurred) cv2.imshow('g_hpf',g_hpf)

    将五张图片对比一下: 原图是: 低通处理之后: 变模糊了,所以也叫高斯模糊 用原图减去低通,得到的是高通的图: 用上面特定的55高通滤波器得到的: 和33滤波器得到的: 注意:这里的滤波处理只能对灰度图像,所以对彩色图片的读取和后面的高斯滤波都有个参数0: img = cv2.imread(‘E:\wms1.jpg’,0) blurred = cv2.GaussianBlur(img,(11,11),0)

    2.Canny边缘检测

    import cv2 import numpy as np img = cv2.imread('E:\\wms1.jpg',0) cv2.imwrite('canny.jpg',cv2.Canny(img,200,300)) img_canny = cv2.imread('canny.jpg') cv2.imshow('Canny',img_canny) cv2.waitKey() cv2.destroyAllWindows()

    3.轮廓检测

    import cv2 import numpy as np img = np.zeros((200,200),dtype = np.uint8) img[50:150,50:150] = 255 #这个时候img是一个(200,200)的矩阵 #构造一个明显的有轮廓的图出来 ret,thresh = cv2.threshold(img,127,255,0) #cv2.threshold()阈值函数,若像素高于127,就赋值为255,否则赋值为0 #返回的ret是阈值127,thresh是结果图,重新赋值之后的矩阵 image,contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) #cv2.findContours()函数来查找检测物体的轮廓 #image此时是(200,200)修改后的图片 #contours是轮廓图索引,是一个列表 #hierarchy是一个四通道的值,每个通道都只有一个数-1 color = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR) #将img转成彩色图,由三通道(200,200,3) img_ = cv2.drawContours(color,contours,-1,(0,255,0),2) #img表示输入的需要画的图片, contours表示轮廓值,-1表示轮廓的索引,(0, 0, 255)表示颜色, 2表示线条粗细 #cv采用BGR,所以(0,255,0)就是绿色的边框 cv2.imshow('contours',img_) cv2.waitKey() cv2.destroyAllWindows()

    cv2.threshhold()函数: 当像素值高于阈值时,我们给这个像素赋予一个新值,否则给他赋予另一个值。 cv2.findContours()函数: cv2.findContours()函数来查找检测物体的轮廓。 该函数有三个参数: 第一个参数是寻找轮廓的图像;

    第二个参数表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口): cv2.RETR_EXTERNAL表示只检测外轮廓 cv2.RETR_LIST检测的轮廓不建立等级关系 cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。 cv2.RETR_TREE建立一个等级树结构的轮廓。

    第三个参数method为轮廓的近似办法 cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1 cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息 cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

    该函数返回三个值:修改后的图片,图片的轮廓,层次

    hierarchy层次: 第一个数:表示同一级轮廓的下个轮廓的编号,如果这一级轮廓没有下一个轮廓,一般是这一级轮廓的最后一个的时候,则为-1

    第二个数:表示同一级轮廓的上个轮廓的编号,如果这一级轮廓没有上一个轮廓,一般是这一级轮廓的第一个的时候,则为-1

    第三个数:表示该轮廓包含的下一级轮廓的第一个的编号,假如没有,则为-1

    第四个数: 表示该轮廓的上一级轮廓的编号,假如没有上一级,则为-1 这里是[-1,-1,-1,-1] contours: 轮廓索引 for c in contours: print© 得到轮廓的四个点: [[[ 50 50]]

    [[ 50 149]]

    [[149 149]]

    [[149 50]]] 4.边界框,最小矩形区域,最小闭圆轮廓 原图:

    import cv2 import numpy as np #所有的轮廓都是画在这个img上面的,所以最后imshow()img可以看到所有画的轮廓 img = cv2.pyrDown(cv2.imread('hammer.jpg',cv2.IMREAD_UNCHANGED)) #这个时候img是一个三通道图(634,618,3) gray_img = cv2.cvtColor(img.copy(),cv2.COLOR_BGR2GRAY) #将彩色图片转为灰度图片,这个时候只有一个通道(634,618) ret,thresh = cv2.threshold(gray_img,127,255,cv2.THRESH_BINARY) #图像二值化处理,只有255和0两个值了 image,contours,hier = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #找轮廓,返回的image是修改之后的图片,contours轮廓索引,heir是层次 #cv2.RETR_EXTERNAL表示只检测外轮廓 #cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标 #例如一个矩形轮廓只需4个点来保存轮廓信息 for c in contours: ###计算一个简单的边界框,绿色 #c是轮廓的每个点的坐标 # print(c) #c有812个坐标 x,y,w,h = cv2.boundingRect(c)#通过这些点画一个矩形,计算w,h # print(cv2.boundingRect(c))#(54, 115, 415, 437) cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)#画轮廓 #轮廓是长方形的由(x,y)(x+w,y+h)确定 #颜色是绿色 #线宽是2 ###计算包围目标的最小矩形区域,红色 rect = cv2.minAreaRect(c) # print(rect) #((256.23016357421875, 309.781494140625), (435.0880432128906, 389.2210388183594), -81.33019256591797) box = cv2.boxPoints(rect) #opencv没有直接从轮廓信息中计算最小矩形轮廓顶点坐标的函数,所以这里单独计算一下点坐标 # print(box) #变成矩形的四个点坐标了 box = np.int0(box) # print(box)#坐标取整 cv2.drawContours(img,[box],0,(0,0,255),3) #这个drawContours函数第二个参数只接受轮廓的数组形式,所以将box变成数组 #检测边界轮廓最小的闭圆,蓝色 (x,y),radius = cv2.minEnclosingCircle(c) #返回一个二元数组,第一个是圆心坐标,第二个是半径值 # print(cv2.minEnclosingCircle(c)) #((299.5028381347656, 325.4397888183594), 246.93551635742188) center = (int(x),int(y)) radius = int(radius) cv2.circle(img,center,radius,(255,0,0),4) img_ = cv2.drawContours(img,contours,-1,(0,255,255),3) #黄色 cv2.imshow('contours',img) cv2.waitKey() cv2.destroyAllWindows()

    4.凸轮廓与Douglas-Peucker算法

    import cv2 import numpy as np #所有的轮廓都是画在这个img上面的,所以最后imshow()img可以看到所有画的轮廓 img = cv2.pyrDown(cv2.imread('hammer.jpg',cv2.IMREAD_UNCHANGED)) #pyrDown这个函数可以将图像降采样和平滑滤波 #缩小了图像的尺寸,将图像尺寸行和列方向缩减一半,不然图片太大了,显示不全 gray_img = cv2.cvtColor(img.copy(),cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(gray_img,127,255,cv2.THRESH_BINARY) image,contours,hier = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #找轮廓,返回的image是修改之后的图片,contours轮廓索引,heir是层次 #cv2.RETR_EXTERNAL表示只检测外轮廓 #cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标 #例如一个矩形轮廓只需4个点来保存轮廓信息 ###画轮廓,黄色 contours_img = cv2.drawContours(img,contours,-1,(0,255,255),2) #黄色 画轮廓 ###画轮廓多变形框,青色 cnt = contours[0] #np.shape(contours)是(1, 812, 1, 2) epsilon = 0.01*cv2.arcLength(cnt,True)#参数越小,多边形越精确 approx = cv2.approxPolyDP(cnt,epsilon,True)#approx是很多点坐标组成的列表 #计算近似的多边形框,有三个参数 #cnt是轮廓 #epsilon是源轮廓与近似多边形的最大差值 #第三个参数表示该多边形是否闭合 cv2.drawContours(img, approx, -1, (255, 255, 0), 5)#把这个多边形框的每个点画出来 cv2.polylines(img, [approx], True, (255, 255, 0), 2)#用直线把这些点连起来 ###标注多边形框的面积和周长 area = cv2.contourArea(approx) length = cv2.arcLength(approx, True) font = cv2.FONT_HERSHEY_SIMPLEX # 设置字体样式 text = 'Area: '+str(int(area))+' Length: '+str(int(length)) cv2.putText(img, text, (10, 30), font, 0.5, (0, 255, 0), 1, cv2.LINE_AA, 0) ###画凸包,红色 hull = cv2.convexHull(cnt)#hull同approx一样是点坐标组成的列表 cv2.drawContours(img, hull, -1, (0,0,255), 5)#把这个凸包框的每个点画出来 cv2.polylines(img, [hull], True, (0,0,255), 2)#用直线把这些点连起来

    如果要单独把这些轮廓,近似多边形,凸包单独显示:

    ###轮廓画到另一张图上 img_ = np.zeros((700,700),dtype = np.uint8) img_ = cv2.cvtColor(img_,cv2.COLOR_GRAY2BGR) #画轮廓,黄色 contours_img = cv2.drawContours(img_,contours,-1,(0,255,255),2) #黄色 画轮廓 #青色,多边形 cv2.drawContours(img_, approx, -1, (255, 255, 0), 5)#把这个多边形框的每个点画出来 cv2.polylines(img_, [approx], True, (255, 255, 0), 3)#用直线把这些点连起来 #红色,凸包 cv2.drawContours(img_, hull, -1, (0,0,255), 5)#把这个凸包框的每个点画出来 cv2.polylines(img_, [hull], True, (0,0,255), 2)#用直线把这些点连起来 cv2.imshow('contours',img_) cv2.waitKey() cv2.destroyAllWindows()

    5.直线检测

    import cv2 import numpy as np img = cv2.imread('line_det.jpg',cv2.IMREAD_UNCHANGED) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray,50,120) ##第二,三个参数是指低阈值low和高阈值high minLineLength = 20 maxLineGap = 5 lines = cv2.HoughLinesP(edges,1.0,np.pi/180,20,minLineLength,maxLineGap) for line in lines: for x1,y1,x2,y2 in line: #print(x1,y1,x2,y2) #lines里面储存着检测到的直线段的首末坐标点 #一个列表里的四个值代表着一条线段 cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2) cv2.imshow('edges',edges) cv2.imshow('lines',img) cv2.waitKey() cv2.destroyAllWindows()

    canny边缘检测函数为什么要有两个阈值呢,低阈值和高阈值

    高的那个阈值是将要提取轮廓的物体与背景区分开来,就像阈值分割的参数一样,是决定目标与背景对比度的。 低的阈值是用来平滑边缘的轮廓,有时高的阈值设置太大了,可能边缘轮廓不连续或者不够平滑,通过低阈值来平滑轮廓线,或者使不连续的部分连接起来。

    T1,T2。大于T1的称为强边界。T1和T2之间的为弱边界。 如果只有强边界,那么边界可能断断续续。而且会少分割。所以弱边界的作用就是解决上面这个问题。如果强边界点的8连通区域内有弱边界点,那么认为该弱边界点为强边界。

    HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None) 参数: image:必须是二值图像,推荐使用canny边缘检测的结果图像; rho:线段以像素为单位的距离精度,double类型的,推荐用1.0; theta:线段以弧度为单位的角度精度,推荐用numpy.pi/180; threshold:累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。根据情况推荐先用100试试; lines:这个参数的意义未知,发现不同的lines对结果没影响,但是不要忽略了它的存在; minLineLength:线段以像素为单位的最小长度,根据应用场景设置; maxLineGap:同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段。

    6.圆检测

    import cv2 import numpy as np planets = cv2.imread('circle_det_.png') gray_img = cv2.cvtColor(planets,cv2.COLOR_BGR2GRAY) img = cv2.medianBlur(gray_img,5)#降噪处理 cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR) circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,120,param1 = 100,param2 = 30,minRadius = 0,maxRadius = 0) #参数1:传递的图片 #参数2:method使用什么方法检测 #参数3:dp,默认为1 #参数4:minDist,不同圆心的最小距离,120,单位为像素 #参数5:param1,设计canny算法,80为canny算法的上限,这里的canny算法下限自动设置为为上限一半 #参数6:param2, #参数7,8:最小半径和最大半径,避免识别物体之外的圈 circles = np.uint16(np.around(circles)) for i in circles[0,:]: print(i) #画圆圈 cv2.circle(planets,(i[0],i[1]),i[2],(0,255,0),2) #画圆心 cv2.circle(planets,(i[0],i[1]),2,(0,0,255),3) cv2.imshow('HoughCircles',planets) cv2.waitKey() cv2.destroyAllWindows()

    我们刚开始没有限制检测出来的圆形大小,然后误检了很多圆: 然后我们将检测的圆限制一下范围半径在20-90之间:

    circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,120,param1 = 100,param2 = 30,minRadius = 20,maxRadius = 90) #参数1:传递的图片 #参数2:method使用什么方法检测 #参数3:dp,默认为1 #参数4:minDist,不同圆心的最小距离,120,单位为像素 #参数5:param1,设计canny算法,80为canny算法的上限,这里的canny算法下限自动设置为为上限一半 #参数6:param2, #参数7,8:最小半径和最大半径,避免识别物体之外的圈

    检测出的圆: [634 630 69] [410 630 69] [176 394 70] [860 394 69] [276 154 69] [634 394 70] [760 160 70] [178 630 69] [400 392 69] [860 628 69] [524 158 89]

    最新回复(0)