【碎片知识(8) · 计算机视觉基础】对图片中Blob的检测和计数

本文探讨了如何使用OpenCV进行Blob检测和计数,通过一个米粒图像实例,介绍了SimpleBlobDetector、getStructuringElement、findContours等关键函数的用法。还讲解了Rect类、vector的使用及其注意事项,为理解计算机视觉中的Blob检测提供了基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文将通过对一幅米粒图像的处理,讨论在OpenCV中相关函数对Blob的检测和计数问题。

OpenCV实例中的代码说明

1)        关于SimpleBlobDetector的说明:

   其目的是对Blob进行侦测,Blob就是在图片中连在一起的一团像素集合,该集合中的像素必须拥有共同的特性(比如灰度值等)。

   OpenCV提供了非常便捷的方法去侦测Blob,并可以基于特定的特征(如颜色、尺寸、形状等)对这些Blob进行筛选,以避免早期的Blob侦测算法中的过度提取。

   具体原理内容在:https://www.learnopencv.com/blob-detection-using-opencv-python-c/

2)        getStructuringElement()函数:

   该函数的第1个参数表示内核的形状

矩形(MORPH_RECT);交叉形(MORPH_CORSS);椭圆形(MORPH_ELLIPSE);

   第2和第3个参数分别是内核的尺寸以及锚点的位置,锚点的位置有默认的(-1, -1),即中心点。

   一般在调用腐蚀(erode)以及膨胀(dilate)函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。

3)        findContours()函数:

   函数原型findContours(InputOutputArray image,OutputArrayOfArrays contours, OutputArray hierarchy, int mode,  int method, Point offset=Point());

Ø  image单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普

#例程序使用OpenMV4,OpenMV4Plus均可 import sensor, image, time from pyb import millis, Pin, Timer, LED sensor.reset() sensor.set_hmirror(False) sensor.set_vflip(False) sensor.set_transpose(False) sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.VGA) sensor.set_windowing([200,120,240,240]) sensor.set_vflip(True) #垂直翻转 sensor.set_hmirror(True) #水平翻转 clock = time.clock() #设置频率,初始化定时器4,将其设置为50HZ,也就是说一个PWM周期为20ms TIM4 = Timer(4, freq=50) ## 生成50HZ方波,使用TIM4,channels 1,2分别是1.5ms脉宽,对应舵机90度 ch1 = TIM4.channel(1, Timer.PWM, pin=Pin("P7"), pulse_width=0) #旋转舵机 ch2 = TIM4.channel(2, Timer.PWM, pin=Pin("P8"), pulse_width=0) #俯仰舵机 P9_Out = Pin('P9', Pin.OUT_PP) P9_Out.low() # 设置P9为低电平,关闭激光 red_led = LED(1) green_led = LED(2) blue_led = LED(3) #控制舵机驱动函数,输入舵机角度 def servo_ctrl(angle_x, angle_y): #1000对应0.5ms,舵机0° ch1.pulse_width(int(1000+4000*angle_x/180)) ch2.pulse_width(int(1000+4000*angle_y/180)) #根据输入激光点与中心点的x,y距离,计算出云台调节的增量,输出要增加的角度 def trace_add(distance): #正值 if distance == 1000.0: return 0.7; #特定的量,云台扫描使用这个角度增量 elif distance > 50.0: return 0.6; elif distance > 40.0: return 0.3; elif distance > 30.0: return 0.2; elif distance > 20.0: return 0.1; elif distance > 10.0: return 0.05; elif distance > 5.0: return 0.03; elif distance > 2.0: return 0.02; #负值 elif distance == -1000.0: return -0.7; #特定的量,云台扫描使用这个角度增量 elif distance < -50.0: return -0.6; elif distance < -40.0: return -0.3; elif distance < -30.0: return -0.2; elif distance < -20.0: return -0.1; elif distance < -10.0: return -0.05; elif distance < -5.0: return -0.03; elif distance < -2.0: return -0.02; return 0.0; #颜色阈值,可以宽泛一些。如果效果不好,则自行调阈值 thr_write = (60, 100, -71, 65, -81, 71)#白色阈值 thr_black = (56, 0, -74, 35, -62, 37)#黑色阈值 thr_purple = (85, 100, -12, 127, -128, 19)#紫色阈值(紫外线激光点) target = [0,0] #目标坐标,自动计算 purple_point = [0,0] #紫色激光点坐标,自动计算 vision_point = [114,125]#视野中心点,在激光未开启时用于追踪,自行调整 distance = [0,0] #激光点与中心的距离 add_angle = [0,0] #舵机增量角度 now_servo = [90,100] #当前舵机角度 flag = [0,0] #检测成功标志,前者为激光,后者为中心点,检测成功为1,否则为0 count = 0; #计数,无目标时云台要旋转寻找目标 direction = [0,0]; #旋转方向,在寻找目标时来回旋转的方向确定 blue_led.on() time.sleep_ms(100) blue_led.off() while(True): timer = millis() clock.tick() img = sensor.snapshot() black_blobs = img.find_blobs([thr_black], merge =False) #找黑框 if black_blobs: #如果有目标 #找紫色激光点 purple_blobs = img.find_blobs([thr_purple], merge=True) if purple_blobs: for blob in purple_blobs: purple_point = [blob[5], blob[6]] #激光中心点 flag[0] = 1 #激光点检测成功标志置1 break else: flag[0] = 0 #激光点检测成功标志置0 #寻找中心点 for single_black_blob in black_blobs: #找到的目标中,符合阈值的面积总的区域之间的比值。因为黑框内部不是黑色,所以这个比值不会很大。 if single_black_blob.pixels() / (single_black_blob.w()*single_black_blob.h()) < 0.3: #img.draw_rectangle(single_black_blob.rect(),color=(0,255,255))#绘制符合条件的区域 #在区域内找白色 write_blobs = img.find_blobs([thr_write],area_threshold=2,roi =single_black_blob.rect(), merge =False) if write_blobs:#如果有目标 largest_white = max(write_blobs, key=lambda b: b.area())#找到最大的块 #绘制黑框的中心点 #判断条件1:黑色区域面积白色区域面积的比例;判断条件2:黑框白色区域中心坐标的差值 if (2 < largest_white.pixels() / single_black_blob.pixels() < 4) and\ abs( largest_white.cx() - single_black_blob.cx() ) < 10 and \ abs( largest_white.cy() - single_black_blob.cy() ) < 10 : target = [largest_white.cx(), largest_white.cy()] #白色区域中心坐标 img.draw_cross(target,color=(255,0,0),thickness=3) #绘制在画布上 flag[1] = 1 #中心点检测成功标志置1 green_led.on() break else: green_led.off() flag[1] = 0 #中心点检测成功标志置0 else: green_led.off() flag[1] = 0 #中心点检测成功标志置0 else: flag[1] = 0 #中心点检测成功标志置0 else: flag[1] = 0 #中心点检测成功标志置0 flag[0] = 0 #激光点检测成功标志置0 #计算误差距离 if flag[1] == 1:#有中心点,追踪视觉中心,小于一定的值打开激光 count = 0; #把计数清零,避免误识别后进入搜索模式 distance[0] = vision_point[0] - target[0] distance[1] = vision_point[1] - target[1] if abs(distance[0]) < 4 and abs(distance[1]) < 4: P9_Out.high() #设置P9为高电平,打开激光 else: P9_Out.low() # 设置P9为低电平,关闭激光 elif flag[1] == 0: #无中心点,旋转搜索黑框中心点 if count > 20: P9_Out.low() # 设置P9为低电平,关闭激光 if direction[0] == 0: #正向自增,增X旋转 distance[0] = 1000 elif direction[0] == 1: #反向自增,增X旋转 distance[0] = -1000 if now_servo[0] >= 120: #到达角度,翻转方向 direction[0] = 1; elif now_servo[0] <= 60: direction[0] = 0; distance[1] = 0 #pitch不自增,直接定角度,距离需要设为0 now_servo[1] = 100 # if direction[1] == 0: #正向自增,增Y俯仰 # distance[1] = 1000 # elif direction[1] == 1: #反向自增,增Y俯仰 # distance[1] = -1000 # if now_servo[1] >= 105: #到达角度,翻转方向 # direction[1] = 1; # elif now_servo[1] <= 95: # direction[1] = 0; elif count < 50: count = count+1 else: distance[0] = 0 distance[1] = 0 #根据中心距离计算舵机增量 add_angle[0] = trace_add(distance[0]) add_angle[1] = trace_add(distance[1]) #将舵机角度增量加到当前舵机角度 now_servo[0] = now_servo[0] + add_angle[0] now_servo[1] = now_servo[1] + add_angle[1] #角度限幅 if now_servo[0] > 140: now_servo[0] = 140 elif now_servo[0] < 40: now_servo[0] = 40 if now_servo[1] > 110: now_servo[1] = 110 elif now_servo[1] < 90: now_servo[1] = 90 #舵机执行 servo_ctrl(now_servo[0],now_servo[1]) #servo_ctrl(90,90) #如需提高帧率,注释掉print # print('舵机X角度',now_servo[0],'舵机Y角度',now_servo[1],'激光标志',flag[0],'中心点标志',flag[1],'目标',target,'激光点',purple_point) # timer = millis() - timer # print('用时',timer,'实时帧速',1000/timer,'平均帧速',clock.fps()) 这些都是什么意思
最新发布
08-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值