【问题标题】:Is there a more efficient way to search an image for a specific pixel?有没有更有效的方法来搜索特定像素的图像?
【发布时间】:2022-12-21 11:59:29
【问题描述】:

我目前正在使用 python 和 openCV 进行计算机视觉项目。

我需要在尺寸约为 620x420 像素的图像中搜索绿色像素,该像素最靠近图像底部。先前轮廓分析的图像中有多个绿色像素(例如 100)。

这是一个示例图像:

我已经使用以下代码实现了它:

    # Search for the most bottom contour point
    xLowestContour = 0                         # x coordinate of the lowest contour point
    yLowestContour = 0                         # y coordinate of the lowest contour point
    for y in range(roi_h):                     # roi_h is the heigth of the image
        for x in range(roi_w):                 # roi_w is the width of the image
            (b, g, r) = roi_copy[y, x]         # roi_copy is the actual image
            if g == 255 and b == 0 and r == 0:
                xLowestContour = x
                yLowestContour = y

此代码有效。但是它有一个大问题。看起来这种在图像中搜索特定像素的方式非常低效。使用此 coden-p,帧速率从 25 FPS 下降到 2 FPS。使用此 coden-p 时 CPU 利用率仅为 10%。

有没有更有效的方法来执行此操作?我还想利用更多的 CPU 能力并获得更高的帧率。

【问题讨论】:

    标签: python numpy opencv image-processing computer-vision


    【解决方案1】:

    避免循环,使用numpy

    你可以使用 numpy 的argwhere()。该语句返回满足特定条件的坐标数组。

    a = np.argwhere(image == [0,255,0])
    
    # returns array a with three columns,
    # since you need only the coordinates, you can delete the third column:
    a = np.delete(a, 2, 1)  # delete third column in a
    

    a 将返回以下示例:

    array([[  0,  18],
           [  0,  19],
           [  0,  21],
           ...,
           [539, 675],
           [539, 677],
           [539, 677]], dtype=int64)
    

    上面的代码返回存在绿色像素值 [0, 255, 0] 的坐标。

    根据返回的坐标,您可以过滤靠近图像底部的坐标。

    【讨论】:

    • 它基本上有效。但有时 np.argwhere(...) 从像素返回一些像素坐标,这些像素不是绿色的。你知道什么会导致这种奇怪的行为吗?
    • @michael 分享这些图片会有所帮助
    【解决方案2】:
    def get_last_pixel(image, rgb):
        color_rows, color_cols = np.where((image[:, :, 0] == rgb[0]) & (image[:, :, 1] == rgb[1]) & (
            image[:, :, 2] == rgb[2]))  # Get coordinates of pixels wit the requested rgb value
        if len(color_rows) == 0:
            raise Exception("no pixels of the requested color")
        row_max = np.argmax(color_rows)  # Find the index of the biggest row
        pixel_coords = (color_rows[row_max], color_cols[row_max])
        return pixel_coords
    
    
    example_input = cv2.imread("example_input.png")
    last_green = get_last_pixel(example_input, (0, 255, 0))
    example_output = cv2.circle(
        example_input, last_green[::-1], radius=5, color=(0, 0, 255), thickness=1)
    cv2.imwrite("example_output.png", example_output)
    

    输入示例:

    示例输出:

    【讨论】:

      【解决方案3】:

      接受的答案中存在一个明显的问题,因为我们可以看到的最后一个坐标是 [539, 677]。嗯,在 (539,677) 和 (677,539) 处都没有绿色像素。这里的问题是,它检查条件是否在 RGB 值的某处为真。如果只有一个值为 True,则所有其他值也为 True。

      def argwhere(pic, color):
          a = np.argwhere(pic == color)
          return np.delete(a, 2, 1)
      
      
      
      exa2 = argwhere(pic=img, color=[0, 255, 0])
      for e in exa2:
          if np.sum(img[e[0], e[1]]) != 255:
              print(img[e[0], e[1]])
      # ....
      # [0 0 0]
      # [0 0 0]
      # [0 0 0]
      # [0 0 0]
      # [0 0 0]
      # [255 255 255]
      # [255 255 255]
      # [255 255 255]
      # [255 255 255]
      # [255 255 255]
      # [255 255 255]
      # ....
      

      这周,我发现了 Numexpr https://github.com/pydata/numexpr(感谢 Stackoverflow),并编写了一个比 np.argwhere(image == [0,255,0]) 快 5 倍并且可能比 PIL 快 100 倍的小函数。更好的是:它检查所有 RGB 值 :)

      import numexpr
      import numpy as np
      
      
      def search_colors(pic, colors):
          colorstosearch = colors
          red = pic[..., 0]
          green = pic[..., 1]
          blue = pic[..., 2]
          wholedict = {"blue": blue, "green": green, "red": red}
          wholecommand = ""
          for ini, co in enumerate(colorstosearch):
              for ini2, col in enumerate(co):
                  wholedict[f"varall{ini}_{ini2}"] = np.array([col]).astype(np.uint8)
              wholecommand += f"((red == varall{ini}_0) & (green == varall{ini}_1) & (blue == varall{ini}_2))|"
          wholecommand = wholecommand.strip("|")
          expre = numexpr.evaluate(wholecommand, local_dict=wholedict)
          exa = np.array(np.where(expre)).T[::-1]
          return np.vstack([exa[..., 1], exa[..., 0]]).T
      
      
      import cv2
      
      path = r"C:UsersGamerDocumentsDownloadsjFDSk.png"
      img = cv2.imread(path)
      exa1 = search_colors(pic=img, colors=[(0, 255, 0)])
      coords = exa1[np.where(np.max(exa1[..., 1]))[0]]
      print(coords)
      #array([[324,  66]], dtype=int64) 
      

      它非常快:(1 毫秒/Intel i5)

      %timeit search_colors(pic=img, colors=[(0,255,0)])
      1.05 ms ± 7.97 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
      
      %timeit argwhere(pic=img, color=[0, 255, 0])
      4.68 ms ± 63.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-09
        • 2012-08-29
        • 1970-01-01
        • 2017-10-24
        • 2015-10-10
        • 2019-10-09
        • 2015-06-14
        • 1970-01-01
        相关资源
        最近更新 更多