gocv:如何使用opencv从蓝色背景中剪切图像

时间:2018-03-12 17:46:48

标签: opencv go

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

enter image description here

我在OpenCV中使用InRange函数(inRange)来定义蓝色的上限和下限以创建掩码然后CopyToWithMask函数({{3在OpenCV中)在原始图像上应用蒙版。我希望结果是蓝色背景,切出披萨。

代码非常简单:

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)
}

然而,除了略微暗示披萨边缘外,所得到的图像基本上几乎完全是黑色的:

copyTo

至于选择的蓝色上限和下限,我遵循官方enter image description here中提到的指南:

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]作为下限和   上限分别为。

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

3 个答案:

答案 0 :(得分: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)
    }
}

此代码生成我之后的结果:

pizza cut out

我还要添加另一张显示im

的图片

image mask

现在,让我们来看看代码。 gocv API函数InRange()不接受Scalar之类的OpenCV does,因此您必须执行所有疯狂的图片频道拆分和合并舞蹈,因为您需要传递{{1} s为Mat的下限和上限;这些InRange()个掩码必须与您运行Mat的图像具有确切的频道数。

这提出了另一个要点:在InRange()为此任务分配Scalar时,我最初使用gocv类型代表频道颜色 - 对于具有三个频道的gocv.MatTypeCV8U图片来说还不够 - 这是使用HSV类型修复的。

如果我可以将gocv.MatTypeCV8UC3传递到gocv.Scalar,很多锅炉板代码就会消失;所有不必要的gocv.InRange()分配用于分割和重新组合创建下限和上限通道所需的通道。

答案 1 :(得分:2)

enter image description here

具有给定范围的

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中检查这个功能,但我怀疑没有差异。

答案 2 :(得分:1)

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

compiles the best with gcc/clang/MSVC

我先解释一下。

(1)图像已变为灰色。

(2)应用Canny Edge

(3 - 4)创建内核并用它来进行Dilate和Close操作

(5)找到轮廓

(6)创建并应用了面具

(7)裁剪并保存区域

以下是代码:

{{1}}

希望将帮助多个用户。