Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 识别拐角';带有openCV的s页面部分失败_Python_Opencv_Image Processing - Fatal编程技术网

Python 识别拐角';带有openCV的s页面部分失败

Python 识别拐角';带有openCV的s页面部分失败,python,opencv,image-processing,Python,Opencv,Image Processing,我想得到一页的四个角, 我采取的步骤: 转换为灰度 将阈值应用于图像 Canny在边缘检测中的应用 之后,我使用了findContours 为每个多边形绘制近似多边形,我的假设是相关多边形必须有4个顶点 但一路上我发现我的解决方案有时会出错, 显然,我的解决方案不够健壮(可能有点幼稚) 我认为纸张角点检测失败的一些原因是: 手动选取阈值进行canny检测 对于approxPolyDP 我的代码 import cv2 import numpy as np image = cv2.imread

我想得到一页的四个角, 我采取的步骤:

  • 转换为灰度
  • 将阈值应用于图像
  • Canny在边缘检测中的应用
  • 之后,我使用了
    findContours
  • 为每个多边形绘制近似多边形,我的假设是相关多边形必须有4个顶点
  • 但一路上我发现我的解决方案有时会出错, 显然,我的解决方案不够健壮(可能有点幼稚)

    我认为纸张角点检测失败的一些原因是:

    • 手动选取阈值进行canny检测
    • 对于
      approxPolyDP
    我的代码

    import cv2
    import numpy as np
    image = cv2.imread('page1.jpg') 
    
    descalingFactor = 3
    imgheight, imgwidth = image.shape[:2]
    resizedImg = cv2.resize(image, (int(imgwidth / descalingFactor), int(imgheight / descalingFactor)),
                            interpolation=cv2.INTER_AREA)
    
    cv2.imshow(winname="original", mat=resizedImg)
    cv2.waitKey()
    
    gray = cv2.cvtColor(resizedImg, cv2.COLOR_BGR2GRAY)
    cv2.imshow(winname="gray", mat=gray)
    cv2.waitKey()
    img_blur = cv2.GaussianBlur(gray, (5, 5), 1)
    cv2.imshow(winname="blur", mat=img_blur)
    cv2.waitKey()
    
    canny = cv2.Canny(gray,
                      threshold1=120,
                      threshold2=255,
                      edges=1)
    
    cv2.imshow(winname="Canny", mat=canny)
    cv2.waitKey()
    
    contours, _ = cv2.findContours(image=canny, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
    
    contours = sorted(contours, key=cv2.contourArea, reverse=True)
    
    for idx, cnt in enumerate(contours):
        # print("Contour #", idx)
        # print("Contour #", idx, " len(cnt): ", len(cnt))
    
        cv2.drawContours(image=resizedImg, contours=[cnt], contourIdx=0, color=(255, 0, 0), thickness=3)
        cv2.imshow(winname="contour" + str(idx), mat=resizedImg)
        conv = cv2.convexHull(cnt)
        epsilon = 0.1 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)
        cv2.drawContours(resizedImg, [approx], 0, (0, 0, 255), 3)
        cv2.waitKey(0)
        if len(approx) == 4:
            print("found the paper!!")
            break
    
    pts = np.squeeze(approx)
    
    另一种方法

    我想知道,将一个具有4个顶点(四边形)的多边形拟合到轮廓上,然后检查多边形与轮廓之间的面积差是否低于指定的阈值,这不是更好的方法吗

    有人能推荐一个更健壮的解决方案吗(用代码演示),谢谢

    图像:

    图1:

    图2:

    图3:


    图4:

    正如fmw42所建议的,您需要更多地限制问题。有太多的变量来构建“在任何情况下都有效”的解决方案。一个可能的、非常基本的解决方案是尝试获取页面的
    凸包

    另一种更稳健的方法是搜索角点的四个顶点,并外推线条,以近似纸张边缘。这样,您就不需要完美、干净的边,因为您可以使用四个(甚至三个)角来重建它们

    要查找顶点,可以在边上运行Hough线检测器角检测器,并获得至少四个可识别的端点/起点簇。由此,您可以对四个簇进行平均,以获得每个角的一对
    (x,y)
    点,并使用这些点外推直线

    对于堆栈溢出问题,该解决方案是假设性的,而且相当费劲,所以让我尝试第一个方案——通过凸包检测。以下是步骤:

  • 对输入图像设置阈值
  • 从输入中获取边
  • 使用最小面积过滤器获取边缘的外部轮廓
  • 获取过滤图像的凸包
  • 获取凸面外壳的角点
  • 让我们看看代码:

    # imports:
    import cv2
    import numpy as np
    
    # image path
    path = "D://opencvImages//"
    fileName = "img2.jpg"
    
    # Reading an image in default mode:
    inputImage = cv2.imread(path + fileName)
    
    # Deep copy for results:
    inputImageCopy = inputImage.copy()
    
    # Convert BGR to grayscale:
    grayInput = cv2.cvtColor(inputImageCopy, cv2.COLOR_BGR2GRAY)
    
    # Threshold via Otsu:
    _, binaryImage = cv2.threshold(grayInput, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    
    第一步是得到一个二进制图像,非常简单。这是通过大津设置阈值的结果:

    尝试从纹理(或高频)背景中分割对象从来都不是一个好主意,但是,在这种情况下,在图像直方图中它是可以识别的,并且二值图像相当好。让我们尝试检测此图像上的边缘,我正在使用与代码相同的参数应用
    Canny

    # Get edges:
    cannyImage = cv2.Canny(binaryImage, threshold1=120, threshold2=255, edges=1)
    
    这就产生了:

    看起来已经足够好了,目标边缘大部分都存在。让我们检测轮廓。我们的想法是设置一个区域过滤器,因为目标轮廓是其他轮廓中最大的。I(启发式)将最小面积设置为
    100000
    像素。一旦找到目标轮廓,我就会得到它的轮廓,如下所示:

    # Find the EXTERNAL contours on the binary image:
    contours, hierarchy = cv2.findContours(cannyImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # Store the corners:
    cornerList = []
    
    # Look for the outer bounding boxes (no children):
    for i, c in enumerate(contours):
    
        # Approximate the contour to a polygon:
        contoursPoly = cv2.approxPolyDP(c, 3, True)
    
        # Convert the polygon to a bounding rectangle:
        boundRect = cv2.boundingRect(contoursPoly)
    
        # Get the bounding rect's data:
        rectX = boundRect[0]
        rectY = boundRect[1]
        rectWidth = boundRect[2]
        rectHeight = boundRect[3]
    
        # Estimate the bounding rect area:
        rectArea = rectWidth * rectHeight
    
        # Set a min area threshold
        minArea = 100000
    
        # Filter blobs by area:
        if rectArea > minArea:
    
            # Get the convex hull for the target contour:
            hull = cv2.convexHull(c)
            # (Optional) Draw the hull:
            color = (0, 0, 255)
            cv2.polylines(inputImageCopy, [hull], True, color, 2)
    
            # Set the corner detection:
            maxCorners = 4
            qualityLevel = 0.01
            minDistance = int(max(height, width) / maxCorners)
    
            # Get the corners:
            corners = cv2.goodFeaturesToTrack(hullImg, maxCorners, qualityLevel, minDistance)
            corners = np.int0(corners)
    
            # Loop through the corner array and store/draw the corners:
            for c in corners:
    
                # Flat the array of corner points:
                (x, y) = c.ravel()
                # Store the corner point in the list:
                cornerList.append((x,y))
    
                # (Optional) Draw the corner points:
                cv2.circle(inputImageCopy, (x, y), 5, 255, 5)
                cv2.imshow("Corners", inputImageCopy)
                cv2.waitKey(0)
            
    
    你会注意到我事先准备了一个列表(
    cornerList
    ),我希望能在其中存储所有的角落。前面代码段的最后两行是可选的,它们通过绘制凸面外壳,这将是生成的图像:

    仍然在循环内部,在我们计算凸包之后,我们将通过
    cv2.goodFeaturesToTrack
    获得角点,它实现了一个角点检测器。函数接收一个二进制图像,因此我们需要准备一个黑色图像,用白色绘制凸包点:

            # Create image for good features to track:
            (height, width) = cannyImage.shape[:2]
            # Black image same size as original input:
            hullImg = np.zeros((height, width), dtype =np.uint8)
    
            # Draw the points:
            cv2.drawContours(hullImg, [hull], 0, 255, 2)
            cv2.imshow("hullImg", hullImg)
            cv2.waitKey(0)
    
    这是图像:

    现在,我们必须设置拐角检测器。它需要您正在寻找的角的数量、丢弃检测为“角”的不良点的最小“质量”参数以及角之间的最小距离。有关更多参数,请查看。让我们设置检测器,它将返回一个点阵列,在那里它检测到一个角点。获取此数组后,我们将在
    角点列表中存储每个点,如下所示:

    # Find the EXTERNAL contours on the binary image:
    contours, hierarchy = cv2.findContours(cannyImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # Store the corners:
    cornerList = []
    
    # Look for the outer bounding boxes (no children):
    for i, c in enumerate(contours):
    
        # Approximate the contour to a polygon:
        contoursPoly = cv2.approxPolyDP(c, 3, True)
    
        # Convert the polygon to a bounding rectangle:
        boundRect = cv2.boundingRect(contoursPoly)
    
        # Get the bounding rect's data:
        rectX = boundRect[0]
        rectY = boundRect[1]
        rectWidth = boundRect[2]
        rectHeight = boundRect[3]
    
        # Estimate the bounding rect area:
        rectArea = rectWidth * rectHeight
    
        # Set a min area threshold
        minArea = 100000
    
        # Filter blobs by area:
        if rectArea > minArea:
    
            # Get the convex hull for the target contour:
            hull = cv2.convexHull(c)
            # (Optional) Draw the hull:
            color = (0, 0, 255)
            cv2.polylines(inputImageCopy, [hull], True, color, 2)
    
            # Set the corner detection:
            maxCorners = 4
            qualityLevel = 0.01
            minDistance = int(max(height, width) / maxCorners)
    
            # Get the corners:
            corners = cv2.goodFeaturesToTrack(hullImg, maxCorners, qualityLevel, minDistance)
            corners = np.int0(corners)
    
            # Loop through the corner array and store/draw the corners:
            for c in corners:
    
                # Flat the array of corner points:
                (x, y) = c.ravel()
                # Store the corner point in the list:
                cornerList.append((x,y))
    
                # (Optional) Draw the corner points:
                cv2.circle(inputImageCopy, (x, y), 5, 255, 5)
                cv2.imshow("Corners", inputImageCopy)
                cv2.waitKey(0)
            
    
    此外,您还可以将角绘制为圆,这将产生以下图像:

    这是在第三张图像上测试的相同算法:


    你需要更好的背景和灯光。光线的反射使得很难从背景中确定页面。使用漫反射照明。此外,纹理背景也很难处理。使用恒定颜色。如果您一次拍摄多页照片(3页和4页有两页),则很容易获得超过4个角。您可以使用cv2.goodFeaturesToTrack()进行角点检测。它可以让您避免角落太近。例如,请参阅
    @stateMachine
    很好的解决方案和很好的解释。@stateMachine非常感谢您为撰写此答案付出的努力和时间!!:)看起来令人印象深刻,现在我更好地掌握了跟踪使用情况的良好特性。我已经彻底阅读了你的解决方案,非常好!照明条件有时会给角点检测带来很大的麻烦,比如:有什么建议,如何克服这种情况?