【问题标题】:gocv: how to cut out an image from blue background using opencvgocv:如何使用opencv从蓝色背景中剪切出图像
【发布时间】:2018-08-20 20:13:27
【问题描述】:

我开始玩gocv。我试图弄清楚一件简单的事情:如何从具有某种颜色背景的图像中剪切出一个对象。在这种情况下,对象是披萨,背景颜色是蓝色。

我使用InRange 函数(OpenCV 中的inRange)定义蓝色的上限和下限以创建蒙版,然后使用CopyToWithMask 函数(OpenCV 中的copyTo)将蒙版应用到原始图像。我希望结果是蓝色背景,上面切掉了比萨饼。

代码很简单:

package main

import (
    "fmt"
    "os"

    "gocv.io/x/gocv"
)

func main() {
    imgPath := "pizza.png"
    // read in an image from filesystem
    img := gocv.IMRead(imgPath, gocv.IMReadColor)
    if img.Empty() {
        fmt.Printf("Could not read image %s\n", imgPath)
        os.Exit(1)
    }
    // Create a copy of an image
    hsvImg := img.Clone()

    // Convert BGR to HSV image
    gocv.CvtColor(img, hsvImg, gocv.ColorBGRToHSV)
    lowerBound := gocv.NewMatFromScalar(gocv.NewScalar(110.0, 100.0, 100.0, 0.0), gocv.MatTypeCV8U)
    upperBound := gocv.NewMatFromScalar(gocv.NewScalar(130.0, 255.0, 255.0, 0.0), gocv.MatTypeCV8U)

    // Blue mask
    mask := gocv.NewMat()
    gocv.InRange(hsvImg, lowerBound, upperBound, mask)

    // maskedImg: output array that has the same size and type as the input arrays.
    maskedImg := gocv.NewMatWithSize(hsvImg.Rows(), hsvImg.Cols(), gocv.MatTypeCV8U)
    hsvImg.CopyToWithMask(maskedImg, mask)

    // save the masked image
    newImg := gocv.NewMat()
    // Convert back to BGR before saving
    gocv.CvtColor(maskedImg, newImg, gocv.ColorHSVToBGR)
    gocv.IMWrite("no_pizza.jpeg", newImg)
}

但是,生成的图像基本上几乎是全黑的,除了一点点披萨边缘:

至于选择的蓝色上下界,我按照官方documentation中提到的指南:

blue = np.uint8([[[255, 0, 0]]])
hsv_blue = cv2.cvtColor(blue, cv2.COLOR_BGR2HSV)
print(hsv_blue)

[[[120 255 255]]]

现在您将 [H-10, 100,100] 和 [H+10, 255, 255] 作为下限和 分别为上限。

我确定我错过了一些基本的东西,但无法弄清楚它是什么。

【问题讨论】:

    标签: opencv go


    【解决方案1】:

    这是我用 Python 做的,因为我不懂 Go...

    让我先解释一下。

    (1) 图像已变为灰色。

    (2) 应用 Canny Edge

    (3 - 4) 创建内核并使用它进行扩张和关闭操作

    (5) 找到轮廓

    (6) 创建和应用蒙版

    (7) 裁剪并保存区域

    代码如下:

    import cv2
    import numpy as np
    
    image = cv2.imread("image.png")
    copy = image.copy()
    
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv2.imshow('Gray', gray)
    cv2.waitKey(0)
    
    edged = cv2.Canny(gray, 10, 250)
    cv2.imshow('Edged', edged)
    cv2.waitKey(0)
    
    kernel = np.ones((5, 5), np.uint8)
    
    dilation = cv2.dilate(edged, kernel, iterations=1)
    cv2.imshow('Dilation', dilation)
    cv2.waitKey(0)
    
    closing = cv2.morphologyEx(dilation, cv2.MORPH_CLOSE, kernel)
    cv2.imshow('Closing', closing)
    cv2.waitKey(0)
    
    # if using OpenCV 4, remove image variable from below
    image, cnts, hiers = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    cont = cv2.drawContours(copy, cnts, -1, (0, 0, 0), 1, cv2.LINE_AA)
    cv2.imshow('Contours', cont)
    cv2.waitKey(0)
    
    mask = np.zeros(cont.shape[:2], dtype="uint8") * 255
    
    # Draw the contours on the mask
    cv2.drawContours(mask, cnts, -1, (255, 255, 255), -1)
    
    # remove the contours from the image and show the resulting images
    img = cv2.bitwise_and(cont, cont, mask=mask)
    cv2.imshow("Mask", img)
    cv2.waitKey(0)
    
    for c in cnts:
        x, y, w, h = cv2.boundingRect(c)
        if w > 50 and h > 130:
            new_img = img[y:y + h, x:x + w]
            cv2.imwrite('Cropped.png', new_img)
    
            cv2.imshow("Cropped", new_img)
            cv2.waitKey(0)
    

    希望能帮助不止一位用户。

    【讨论】:

      【解决方案2】:

      inRange 具有给定的范围非常适合我。我不熟悉 Go,但这是我的 python 代码:

      import numpy as py
      import cv2
      
      img = cv2.imread("pizza.png")
      
      hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
      mask = cv2.inRange(hsv, (110, 100, 100), (130, 255, 255))
      inv_mask = cv2.bitwise_not(mask)
      
      pizza = cv2.bitwise_and(img, img, mask=inv_mask)
      
      cv2.imshow("img", img)
      cv2.imshow("mask", mask)
      cv2.imshow("pizza", pizza)
      cv2.imshow("inv mask", inv_mask)
      cv2.waitKey()
      

      这里有几点说明:

      • inRange 返回 蓝色背景,因此我们需要将其反转以显示对象的蒙版(如果您需要该对象)。
      • 您不需要在hsvImg上应用蒙版并转换为BGR,您可以直接在原始图像上应用蒙版(已经是BGR)。
      • Python 没有CopyToWithMask,所以我使用等效的bitwise_and。您可以在 Go 中检查此功能,但我怀疑不会有任何差异。

      【讨论】:

      • 是的,你是对的,我在python 中编写了非常类似的代码来完成这项工作:我忘了在我的问题中提到,在 Python 中执行此操作是正如你所展示的那样很容易。我的问题与gocvCopyToWithMask 后面的opencv copyTo() 函数更相关
      • @gyre 另一种可能的解决方案是将mask 转换为BRG 和bitwise_and 与原始图像。
      【解决方案3】:

      所以我花了很多时间来弄清楚我错过了什么,最后找到了我的问题的答案,以防万一有人感兴趣。我现在更清楚为什么这个问题没有得到回答,因为gocv API 的解决方案相当疯狂。

      为了得到我想要的结果,我必须编写以下代码:

      package main
      
      import (
          "fmt"
          "os"
          "path/filepath"
      
          "gocv.io/x/gocv"
      )
      
      func main() {
          // read image
          pizzaPath := filepath.Join("pizza.png")
          pizza := gocv.IMRead(pizzaPath, gocv.IMReadColor)
          if pizza.Empty() {
              fmt.Printf("Failed to read image: %s\n", pizzaPath)
              os.Exit(1)
          }
          // Convert BGR to HSV image (dont modify the original)
          hsvPizza := gocv.NewMat()
          gocv.CvtColor(pizza, &hsvPizza, gocv.ColorBGRToHSV)
          pizzaChannels, pizzaRows, pizzaCols := hsvPizza.Channels(), hsvPizza.Rows(), hsvPizza.Cols()
          // define HSV color upper and lower bound ranges
          lower := gocv.NewMatFromScalar(gocv.NewScalar(110.0, 50.0, 50.0, 0.0), gocv.MatTypeCV8UC3)
          upper := gocv.NewMatFromScalar(gocv.NewScalar(130.0, 255.0, 255.0, 0.0), gocv.MatTypeCV8UC3)
          // split HSV lower bounds into H, S, V channels
          lowerChans := gocv.Split(lower)
          lowerMask := gocv.NewMatWithSize(pizzaRows, pizzaCols, gocv.MatTypeCV8UC3)
          lowerMaskChans := gocv.Split(lowerMask)
          // split HSV lower bounds into H, S, V channels
          upperChans := gocv.Split(upper)
          upperMask := gocv.NewMatWithSize(pizzaRows, pizzaCols, gocv.MatTypeCV8UC3)
          upperMaskChans := gocv.Split(upperMask)
          // copy HSV values to upper and lower masks
          for c := 0; c < pizzaChannels; c++ {
              for i := 0; i < pizzaRows; i++ {
                  for j := 0; j < pizzaCols; j++ {
                      lowerMaskChans[c].SetUCharAt(i, j, lowerChans[c].GetUCharAt(0, 0))
                      upperMaskChans[c].SetUCharAt(i, j, upperChans[c].GetUCharAt(0, 0))
                  }
              }
          }
          gocv.Merge(lowerMaskChans, &lowerMask)
          gocv.Merge(upperMaskChans, &upperMask)
          // global mask
          mask := gocv.NewMat()
          gocv.InRange(hsvPizza, lowerMask, upperMask, &mask)
          // cut out pizza mask
          pizzaMask := gocv.NewMat()
          gocv.Merge([]gocv.Mat{mask, mask, mask}, &pizzaMask)
          // cut out the pizza and convert back to BGR
          gocv.BitwiseAnd(hsvPizza, pizzaMask, &hsvPizza)
          gocv.CvtColor(hsvPizza, &hsvPizza, gocv.ColorHSVToBGR)
          // write image to filesystem
          outPizza := "no_pizza.jpeg"
          if ok := gocv.IMWrite(outPizza, hsvPizza); !ok {
              fmt.Printf("Failed to write image: %s\n", outPizza)
              os.Exit(1)
          }
          // write pizza mask to filesystem
          outPizzaMask := "no_pizza_mask.jpeg"
          if ok := gocv.IMWrite(outPizzaMask, mask); !ok {
              fmt.Printf("Failed to write image: %s\n", outPizza)
              os.Exit(1)
          }
      }
      

      这段代码产生了我想要的结果:

      我还要添加另一张显示我的图片

      现在,让我们开始编写代码。 gocv API 函数 InRange() 不接受 Scalar 像 OpenCV does 所以你必须做所有疯狂的图像通道分割和合并舞蹈,因为你需要传入 Mats 作为 @ 的下限和上限987654332@;这些Mat 掩码必须具有与运行InRange() 的图像相同的通道数。

      这带来了另一个重要的一点:在为这个任务分配gocv 中的Scalars 时,我最初使用了gocv.MatTypeCV8U 类型,它代表单个 通道颜色-对于@987654338 来说还不够@ 具有三个通道的图像 -- 这可以通过使用 gocv.MatTypeCV8UC3 类型来修复。

      如果我有可能将gocv.Scalars 传入gocv.InRange(),那么很多样板代码都会消失;所有不必要的gocv.NewMat()分配也是如此,用于拆分和重新组装创建下限和上限通道所需的通道。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-10-02
        • 1970-01-01
        • 2016-04-25
        • 2022-11-17
        • 2016-06-08
        • 2019-03-31
        • 2021-12-24
        • 2020-12-05
        相关资源
        最近更新 更多