用Go进行PNG编码很慢

5
我正在使用go-opencv从内置网络摄像头中获取帧。获取图像所需的时间大约为50毫秒。将其编码为PNG需要大约300毫秒。虽然JPEG的编码速度快3倍,但仍然非常慢。
为什么它运行得这么慢?
注意: 我已经在同一台机器上针对NodeJS编写了类似的代码,并且在进行额外的图像处理时没有任何问题,我认为这排除了硬件问题。
我的代码如下:
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")
        }
    }

}

1
考虑使用imwrite仅保存mat。任何性能差异可能是由于副本或PNG编码器上不同的默认质量设置引起的。 - Mitch
看看如果你在for循环内部声明缓冲区(使用var buffer bytes.Buffer而不是new(bytes.Buffer)),会发生什么。 - dlowe
@DanMašek, Mitch -- 感谢你们的建议。image/png是一个go库,我还没有深入研究它的实现。我可能最终会这样做。现在我正在直接使用Mat进行查看。仅仅采集就需要50毫秒(最多20fps),这让我有些担忧。 - AJ X.
@dlowe 我尝试了一下,但结果微不足道。感谢您的建议! - AJ X.
我正在尝试在我的Windows电脑上设置opencv以进行一些测试,但是遇到了问题......源Forge全天都无法使用,而且我没有安装mingw。 - navossoc
显示剩余6条评论
2个回答

7

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

我还尝试了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像素的随机截图进行测试的,不确定与照片相比会有多大差异。结果可能因人而异。

$ 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

“有趣的是,没有压缩比使用压缩时会导致更多的分配。”

这个答案不仅回答了问题,而且提供了大量的信息以帮助我朝着正确的方向前进。感谢你为此付出的所有努力! - AJ X.

-1
为什么运行这么慢?
测量一下!Go语言内置了很好的性能分析工具,例如在go test中。
最有可能的原因是PNG编码器是纯粹的、未经优化的Go代码。如果速度对你来说还不够快,你可以考虑贡献一个更快的PNG编码器。

5
这应该作为评论添加,而不是单独发布。 - Tarik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接