-3

在此处输入图像描述我正在使用带有 pi 相机的 raspberry pi4 (8GB) 来检测水位。我已经定义了一条从 0,375 到 800,375 的线。如果水位等高线的最高点高于这条线,那么我想调用一个函数。这是我的代码和附加的设置图像。如何仅获得水位轮廓。是否需要对轮廓进行精明的边缘检测才能获得清澈的水位?首先我得到最大的轮廓,然后定义它的最高点。

import numpy as np
import cv2
import time 
from datetime import datetime
    
    #color=(255,0,0)
    color=(0,255,0)
    thickness=2
    kernel = np.ones((2,2),np.uint8) # added 01/07/2021
    picflag = 0 # set value to 1 once picture is taken
    
    # function to take still picture when water level goes beyond threshold
    
    def takepicture(frame):
      currentTime = datetime.now()
      picTime = currentTime.strftime("%d.%m.%Y-%H%M%S") # Create file name for our picture
      text = currentTime.strftime("%d.%m.%Y-%H:%M:%S") 
      font = cv2.FONT_HERSHEY_SIMPLEX # font
      org = (05, 20) # org
      fontScale = 0.5 # fontScale
      color = (0, 0, 255) # Red color in BGR
      thickness = 1 # Line thickness of 2 px
      picName = picTime + '.png'
      image = cv2.putText(frame, text, org, font, fontScale, color, thickness, cv2.LINE_AA, False)
      cv2.imwrite(picName , image)
      picflag = 1
      return 
    
    cap = cv2.VideoCapture(0)
    while(True):
        # Capture frame-by-frame
        ret, frame = cap.read()  # ret = 1 if the video is captured; frame is the image
            
        # Our operations on the frame come here    
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        #blur = cv2.GaussianBlur(gray,(21,21),0)
        gray= cv2.medianBlur(gray, 3)   #to remove salt and paper noise 
    
        #ret,thresh = cv2.threshold(gray,10,20,cv2.THRESH_BINARY_INV)
        ret,thresh = cv2.threshold(gray,127,127,cv2.THRESH_BINARY_INV) 
        thresh = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel) # get outer boundaries only added 01/07/2021
        thresh = cv2.dilate(thresh,kernel,iterations = 5) # strengthen weak pixels added 01/07/2021
        img1, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)   
        #img1,contours,hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) #added 01/07/2021
        cv2.line(frame, pt1=(0,375), pt2=(800,375), color=(0,0,255), thickness=2) # added 01/07/2021
        if len(contours) != 0:
                c = max(contours, key = cv2.contourArea) # find the largest contour
                #x,y,w,h = cv2.boundingRect(c)          # get bounding box of largest contour
                img2=cv2.drawContours(frame, c, -1, color, thickness) # draw largest contour
                #img2=cv2.drawContours(frame, contours, -1, color, thickness) # draw all contours
                #img3 = cv2.rectangle(img2,(x,y),(x+w,y+h),(0,0,255),2)  # draw red bounding box in img
                #center = (x, y)
            #print(center) 
                left = tuple(c[c[:, :, 0].argmin()][0])
                right = tuple(c[c[:, :, 0].argmax()][0])
                top = tuple(c[c[:, :, 1].argmin()][0])
                bottom = tuple(c[c[:, :, 1].argmax()][0])
                # Draw dots onto frame
                cv2.drawContours(frame, [c], -1, (36, 255, 12), 2)
                cv2.circle(frame, left, 8, (0, 50, 255), -1)
                cv2.circle(frame, right, 8, (0, 255, 255), -1)
                cv2.circle(frame, top, 8, (255, 50, 0), -1)
                cv2.circle(frame, bottom, 8, (255, 255, 0), -1)
                
                #print('left: {}'.format(left))
                #print('right: {}'.format(right))
                #print(format(top))
                top_countour_point = top[1]
                print(top_countour_point)
                #print('bottom: {}'.format(bottom))
                #if ((top_countour_point <= 375) and (picflag == 0)):   #checking if contour top point is above line
            #takepicture(frame)
                    #continue
                #if ((top_countour_point > 375) and (picflag == 0)) : 
                    #picflag = 0
                    #continue
        # Display the resulting image
        # cv2.line(frame, pt1=(0,375), pt2=(800,375), color=(0,0,255), thickness=2) # added 01/07/2021
        #cv2.imshow('Contour',img3)
        #cv2.imshow('thresh' ,thresh)
        cv2.imshow('Contour',frame)
       
        if cv2.waitKey(1) & 0xFF == ord('q'):  # press q to quit
           break
    
    # When everything done, release the capture
    cap.release()
    cv2.destroyAllWindows()
4

1 回答 1

0

注意事项

正如评论中指出的那样,根据您的帖子,几乎没有什么可使用的。总的来说,我同意 s0mbre 的观点,你最好使用水位传感器,而 kavko 则认为,如果你确实想使用相机,你需要更好地控制你的环境,包括照明、相机角度、背景、等等

但是,这并不是说您当前的设置不可能,假设它是一个静态设置,除了水位。因此,我们可以做出一些假设。

以下是我为获得近似方法而采取的步骤:

收集图像数据

你只提供了一张已经有线条的图像,所以这并不多。我所做的是删除了您添加的行:

没有线条的原始图像

幸运的是,之后没有留下太多的线路。

图像处理:

我已经加载了图像(这将来自您发布的代码)。

使用上面的假设,我决定专注于图像的一小部分。我只选择了中间的 60 像素 (1)

slc = frame[:, 300:360]

接下来,我已将其转换为灰度 (2)

gray_slc = cv2.cvtColor(slc, cv2.COLOR_BGR2GRAY)

我使用 Canny 边缘检测(请参阅此处的文档)来查找图像中的边缘 (3)

edges = cv2.Canny(gray_slc, 50, 90)

之后,我应用了霍夫变换,以找到所有边缘(相关堆栈溢出答案)(4)

rho = 1
theta = np.pi / 180
threshold = 15
min_line_length = 50
max_line_gap = 20

lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]), min_line_length, max_line_gap)

假设我可以假设所有顶部的线都是容器的边缘,我平均了这些线的 y 坐标,并选择了最低的一个作为水位:

y_avgs = [(line[0, 1] + line[0, 3]) / 2 for line in lines]

water_level = max(y_avgs)

有了这个,我刚刚检查了它是否超过了您选择的阈值:

trigger_threshold = 375
    
if water_level > trigger_threshold:
    print("Water level is under the selected line")

操作结果

现在,请记住,我只有一张图片可以继续。考虑到照明条件,您的结果可能会有所不同。

于 2021-07-12T11:00:47.007 回答