【问题标题】:PNG encoding with Go is slow使用 Go 进行 PNG 编码很慢
【发布时间】:2018-03-08 07:26:10
【问题描述】:

我正在使用 go-opencv 从我的内置网络摄像头获取帧。从相机获取图像所需的时间约为 50 毫秒。对 PNG 进行编码的时间约为 300 毫秒。 JPEG 编码速度快了 3 倍,但速度仍然非常慢。

为什么运行这么慢?

注意: 我已经针对 NodeJS 编写了类似的代码,在同一台机器上执行,并且通过额外的图像处理达到 30fps 绝对没有问题。对我来说,这消除了等式中的硬件问题。

我的代码如下所示:

import (
    "fmt"
    "image/png"

    "github.com/lazywei/go-opencv/opencv"
)

camera := opencv.NewCameraCapture(0)
if camera == nil {
    panic("Unable to open camera.")
}
defer camera.Release()

for {
    if camera.GrabFrame() {
        img := camera.RetrieveFrame(1)
        if img != nil {
            frame := img.ToImage()
            buffer := new(bytes.Buffer)
            png.Encode(buffer, frame)
        } else {
            fmt.Println("Unable to capture frame")
        }
    }

}

【问题讨论】:

  • 考虑使用imwrite 保存垫子。任何性能差异都可能是由于复制或 PNG 编码器上不同的默认质量设置造成的。
  • image/png 究竟是如何实现的? |我会接受@Mitch 的建议,因为 OpenCV 在后台使用了 LibPNG。当您能够获得 30fps 时,每张图像 300 毫秒似乎有点过分。
  • 看看如果你在 for 循环中声明缓冲区(使用 var buffer bytes.Buffer not new(bytes.Buffer))会发生什么。
  • @DanMašek,Mitch -- 感谢您的建议。 image/png 是一个 go 库,我还没有深入研究它的实现。我可能最终会这样做。我现在正在研究直接使用 Mat。仅采集就需要 50 毫秒(最多约 20 fps)这一事实引起了我的担忧。
  • @dlowe 我试了一下,但结果可以忽略不计。感谢您的建议!

标签: opencv go


【解决方案1】:

为什么运行这么慢?

测量! Go 内置了很好的分析工具,例如进入go test

很可能是纯“未优化”Go 的 PNG 编码器。如果这对您来说不够快,您可能需要考虑提供更快的 PNG 编码器。

【讨论】:

  • 这应该作为评论而不是单独的帖子添加。
【解决方案2】:

禁用压缩可将我机器上的编码性能提高一个数量级。如果您不想在标准库之外寻找 png 包,这可能是一个开始。

我也尝试过the BufferPool(Go 1.9 中的新功能),但与具有 nil BufferPool 的编码器相比,它并没有什么不同。也许我做错了。不幸的是,文档非常、呃、简洁。

package main

import (
    "bytes"
    "image"
    "image/png"
    "os"
    "testing"
)

func BenchmarkPNG_Encode(b *testing.B) {
    img, buf := loadImage(b)

    b.ResetTimer()

    for i := 0; i < b.N; i++ {
            buf.Reset()
            png.Encode(buf, img)
    }
}

func BenchmarkPNG_Encoder(b *testing.B) {
    img, buf := loadImage(b)
    enc := &png.Encoder{}

    b.ResetTimer()

    for i := 0; i < b.N; i++ {
            buf.Reset()
            enc.Encode(buf, img)
    }
}

func BenchmarkPNG_Encoder_NoCompression(b *testing.B) {
    img, buf := loadImage(b)
    enc := &png.Encoder{
            CompressionLevel: png.NoCompression,
    }

    b.ResetTimer()

    for i := 0; i < b.N; i++ {
            buf.Reset()
            enc.Encode(buf, img)
    }
}

func loadImage(b *testing.B) (image.Image, *bytes.Buffer) {
    // foo.png PNG 1920x1053 1920x1053+0+0 8-bit sRGB 251KB 0.000u 0:00.000
    f, err := os.Open("foo.png")
    if err != nil {
            b.Fatal(err)
    }

    img, err := png.Decode(f)
    if err != nil {
            b.Fatal(err)
    }
    f.Close()

    buf := &bytes.Buffer{}
    // grow the buffer once
    (&png.Encoder{CompressionLevel: png.NoCompression}).Encode(buf, img)

    return img, buf
}

同样,这是在我的机器上使用大约 1920x1080 像素的图像——随机截图;不确定这与照片有多大不同。 YMMV。

$ go test -v -bench . -benchmem 
goos: linux
goarch: amd64
BenchmarkPNG_Encode-8                         10         119289121 ns/op          884964 B/op         38 allocs/op
BenchmarkPNG_Encoder-8                        10         118001658 ns/op          884932 B/op         37 allocs/op
BenchmarkPNG_Encoder_NoCompression-8         100          13050664 ns/op          807156 B/op        212 allocs/op

有趣的是,没有压缩比压缩导致更多的分配。

【讨论】:

  • 这个答案不仅回答了这个问题,而且还有丰富的信息让我朝着正确的方向前进。感谢您为此付出的所有努力!
猜你喜欢
  • 2021-11-07
  • 2020-06-15
  • 1970-01-01
  • 2017-12-04
  • 2017-12-08
  • 2017-07-26
  • 2011-11-25
  • 1970-01-01
  • 2012-07-03
相关资源
最近更新 更多