opencv二值化找轮廓检测背景简单小物体

一、前言

        本篇主要是针对背景简单,且具有固定颜色的单类小物体,方法为在灰度化时选用图片的HSV中的S通道,再使用opencv 二值化找轮廓大法可将小物体框出。

        原理很简单,图片-》取S通道灰度化-》OTSU二值化-》findcontours找到轮廓

二、代码

import cv2
import numpy as np

import imutils

 
IDX =  0  #选择第几张图片

 
def process( img    ):
    
    img_show = img.copy()
    
    #做灰度化,颗粒具有特定的颜色,转为HSV,取S通道作为灰度图像
    img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    gray = img_hsv[ :,:,1 ]
    
    #采用OTSU 自动阈值二值化做二值化,颗粒部分为白色255,背景为黑色0
    _, th = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    
    #开运算,被定义为先腐蚀后膨胀,作用是去掉一些白色噪声点,防止将白色噪声点框出
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))  # 矩形结构
    binary = cv2.morphologyEx(th, cv2.MORPH_OPEN, kernel)  #开运算
 
    #找轮廓大法,即找到颗粒部分,也即白色部分的最小外接矩形,带方向角
    cnts = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    #cnts = cnts[1] if imutils.is_cv3() else cnts[0]
    cnts = cnts[0]  # 
    
    particle = []
    all_area  , all_v = 0. , 0.
    
    if IDX == 0: #图片字体大小设置,第0张图的分辨率小,所以字体要小些,后2张字体大些
        ws = 0.8 
    else:
        ws = 1.6
    
    x_thr , y_thr = 10, 10  #边框的x或y长度不能小于这2个阈值,滤除一些小噪点干扰
    if len(cnts) > 0:
        c = 1 #颗粒序号
        for cnt in cnts  : #开始遍历找到的所有白色边框
            x_coord = cnt[:, :, 0]
            y_coord = cnt[:, :, 1]
            
            xmin = int(np.min(x_coord))
            xmax = int(np.max(x_coord))
            ymin = int(np.min(y_coord))
            ymax = int(np.max(y_coord))
   
            rect = cv2.minAreaRect(cnt) #最小外接矩形,带方向角的
            box = cv2.cv.Boxpoints() if imutils.is_cv2()else cv2.boxPoints(rect)
            box = np.int0(box)
            
            # 两条边有任何一条是小于10像素的,都不算是颗粒,不做统计
            if np.abs( xmax-xmin ) < x_thr or np.abs( ymax-ymin ) <y_thr:continue
            
            #最小外接矩形,画出4条边
            color =[(255,0,0),(0,255,0),(0,0,255),(0,0,0)]
            for i  in range(3):
                cv2.line(img_show,tuple(box[i]),tuple( box[i+1] ),color[i],3) 
            cv2.line(img_show,tuple(box[3]),tuple( box[0] ),color[3],3)
            
            #需要获取这4个点的长边和短边
            #很简单,只需计算4条边,各有2条边长度是差不多,取他们的平均值,就能获得长边和短边
            lines = []
            for i in range(3):
                lines.append(  np.sqrt( ( box[i][0]-box[i+1][0] )**2+( box[i][1]-box[i+1][1] )**2   )  )   
            lines.append(  np.sqrt( ( box[3][0]-box[0][0] )**2+( box[3][1]-box[0][1] )**2   )  ) 
            
            lines_sort = np.sort(lines)  #排序,前2个是短边,后2个是长边,取平均
            s = ( lines_sort[0]+lines_sort[1] )/2  #短边,取平均值
            #h = ( lines_sort[1]+lines_sort[2] )/2  #长边,取平均值
            h = ( lines_sort[2]+lines_sort[3] )/2  #长边,取平均值
            
            
            #计算面积,体积
            area = s*h 
            v = np.pi*(s**2)*h
            all_area+=area  
            all_v+=v
            
            
            particle.append( [ area,v ] )  #每个颗粒的面积和体积保存于list
            
            x , y = int((xmin+xmax)/2) , int((ymin+ymax)/2)  #在图上画出每个颗粒的序号
            cv2.putText(img_show,  "%d"%c, ( xmin ,y ), 0, ws, (255, 0, 255), 2 )
           
            print("颗粒序号:" , c  , "面积:" , area  ,"体积:", v )
            c+=1 #颗粒序号自加1
            
    n = len( particle )
    print("颗粒数:", n , "总面积:",all_area , "总体积:", all_v )
    
    str_0 = "num:%d all_area:%.2f all_volume:%.2f"%( int(n) , all_area , all_v)
    cv2.putText(img_show,  str_0 ,  (10,200), 0, ws, (255, 0, 255), 2 )
        
    
    cv2.namedWindow("img" , 2)
    cv2.imshow("img" , img_show )
    cv2.namedWindow("th" , 2)
    cv2.imshow("th" , th)
    cv2.namedWindow("binary" , 2)
    cv2.imshow("binary" , binary)
    
    
 
if __name__ == "__main__":
    pic_name =[ "1.jpg" , "2.bmp" , "3.bmp" ,"4.bmp" ]
    
    if IDX in [ 0,1,2 ,3]:
        img = cv2.imread( pic_name[ IDX ] )
 
        process(  img   )
        
        cv2.waitKey() 
        cv2.destroyAllWindows()

三、结果

         可以看到,对于这种背景简单的,取HSV中的S做灰度化二值化,效果非常不错,再用找轮廓大法,可以获得带方向的最小外接矩形,故可将每条边找到,或者框起来...

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值