从 Golang image.Image 检索像素数据时,img.At() 或 rgba.Pix() 更快/更好吗?

Ste*_*ce7 2 go

阅读Get a Pixel array from from golang image.Image 的答案,我发现有两种像素 RGBA 检索方法,viaimg.At()rgba.Pix()

使用哪个更好?是否应该始终使用其中一种,或者是否存在应使用其中一种而不是另一种的情况,反之亦然?

git*_*tdo 6

如果您的程序将在需要大多数(如果不是全部)像素数据的情况下进行计算,那么其rgba.Pix()性能将显着优于img.At(). 如果您只需要图像中单个或几个像素的像素数据,请使用img.At()(在这种情况下计算先决条件的开销rgba.Pix()太高)。

\n

以下是各种测试负载的结果,每个负载的持续时间是 10 个样本的平均值。

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n
方法1x11000x6673840x21601000x667 + 计算1000x667 仅 5x5 访问
img.At()195纳秒30.211071毫秒294.885396毫秒853.345043毫秒42.431 \xce\xbcs
rgba.Pix()719纳秒7.786029毫秒77.700552毫秒836.480063毫秒6.791461毫秒
\n
\n

我们可以看到如何对于微小的 1x1 图像以及我们将 for 循环限制为上限 5 的图像,从而使用img.At()更快的执行时间。但是,对于获取每个像素的用例,rgba.Pix()会带来更好的性能。我们对每个像素进行的计算越多,这种性能改进就越不明显,因为总时间增加,并且img.At()和之间的差异rgba.Pix()变得不那么明显,如“ 1000x667 + 计算”中所示不那么明显,如上表中的

\n

这是使用的测试代码:

\n
func main() {\n    resp, err := http.Get("IMAGE URL GOES HERE")\n    if err != nil {\n        panic(err)\n    }\n    defer resp.Body.Close()\n\n    img, _, err := image.Decode(resp.Body)\n    if err != nil {\n        panic(err)\n    }\n\n    var start time.Time\n    var duration time.Duration\n    samples := 10\n    var sum time.Duration\n\n    fmt.Println("Samples: ", samples)\n    sum = time.Duration(0)\n    for i := 0; i < samples; i++ {\n        start = time.Now()\n        usingAt(img)\n        duration = time.Since(start)\n        sum += duration\n    }\n    fmt.Println("*** At avg: ", sum/time.Duration(samples))\n\n    sum = time.Duration(0)\n    for i := 0; i < samples; i++ {\n        start = time.Now()\n        usingPix(img)\n        duration = time.Since(start)\n        sum += duration\n    }\n    fmt.Println("*** Pix avg: ", sum/time.Duration(samples))\n}\n\nfunc usingAt(img image.Image) {\n    bounds := img.Bounds()\n    width, height := bounds.Max.X, bounds.Max.Y\n\n    for y := 0; y < height; y++ {\n        for x := 0; x < width; x++ {\n            r, g, b, _ := img.At(x, y).RGBA()\n            _ = uint8(r >> 8)\n            _ = uint8(g >> 8)\n            _ = uint8(b >> 8)\n        }\n    }\n}\n\nfunc usingPix(img image.Image, targetColor colorful.Color) {\n    bounds := img.Bounds()\n    width, height := bounds.Max.X, bounds.Max.Y\n\n    rgba := image.NewRGBA(bounds)\n    draw.Draw(rgba, bounds, img, bounds.Min, draw.Src)\n    for y := 0; y < height; y++ {\n        for x := 0; x < width; x++ {\n            index := (y*width + x) * 4\n            pix := rgba.Pix[index : index+4]\n            _ = pix[0]\n            _ = pix[1]\n            _ = pix[2]\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

1000x667仅访问5x5,height将for循环中的and替换width为5和5,限制了访问的像素数量。

\n

1000x667+计算实际上使用了RGB值,通过将每个像素与目标颜色的颜色距离与go-colorful的DE2000计算进行比较。

\n