我开始玩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)
}
Run Code Online (Sandbox Code Playgroud)
然而,生成的图像基本上几乎是全黑的,除了一点披萨边缘的痕迹:
至于蓝色的上限和下限的选择,我遵循了官方文档中提到的指南:
blue = np.uint8([[[255, 0, 0]]])
hsv_blue = cv2.cvtColor(blue, cv2.COLOR_BGR2HSV)
print(hsv_blue)
[[[120 255 255]]]
Run Code Online (Sandbox Code Playgroud)
现在您分别将 [H-10, 100,100] 和 [H+10, 255, 255] 作为下限和上限。
我确信我错过了一些基本的东西,但无法弄清楚它是什么。
因此,我花了相当多的时间来弄清楚我错过了什么,并最终找到了我的问题的答案,以防有人感兴趣。现在我更清楚为什么这个问题还没有得到解答,因为由于gocvAPI,它的解决方案相当疯狂。
这是我必须编写的代码才能得到我想要的结果:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码产生了我想要的结果:
我还将添加另一张显示即时消息的图片
现在,让我们开始编写代码。gocvAPI 函数不像 OpenCV那样InRange()接受,因此您必须执行所有疯狂的图像通道分割和合并操作,因为您需要将s 作为下限和上限传递给; 这些蒙版必须具有与您运行的图像完全相同的通道数。ScalarMatInRange()MatInRange()
这提出了另一个重要的点:在为此任务分配Scalars时,我最初使用表示单通道颜色的类型 - 对于具有三个通道的图像来说不够- 这是通过使用类型来修复的。gocvgocv.MatTypeCV8UHSVgocv.MatTypeCV8UC3
如果我可以将gocv.Scalars传递到gocv.InRange()很多样板代码中,那么这些样板代码就会消失;所有用于gocv.NewMat()分割和重组创建下限和上限通道所需的通道的不必要的分配也会如此。
| 归档时间: |
|
| 查看次数: |
4978 次 |
| 最近记录: |