简体   繁体   English

RGBA到并行Golang中的灰度

[英]RGBA to Grayscale in parallel Golang

I have written a program which converts RGBA image to Grayscale sequentially. 我编写了一个程序,可以将RGBA图像顺序转换为灰度。 I'm now trying to convert it so it runs in parallel. 我现在正在尝试对其进行转换,使其并行运行。

I kind of understand how I need to be doing this but I'm struggling to get started. 我有点理解我需要怎么做,但是我正在努力入门。

Here is what I have so far. 这是我到目前为止所拥有的。

   package main

import (
"image"
"image/color"
"image/jpeg"
"log"
"os"
)

var lum float64


type ImageSet interface {
Set(x, y int, c color.Color)
}

func rgbtogray(r uint32, g uint32, b uint32)  float64{
    lum = 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)
    return lum
}


func main() {
file, err := os.Open("flower.jpg")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

img, err := jpeg.Decode(file)
if err != nil {
    log.Fatal(os.Stderr, "%s: %v\n", "flower.jpg", err)
}

channel1 := make(chan float64)
channel2 := make(chan float64)


b := img.Bounds()
imgSet := image.NewRGBA(b)

halfImage := b.Max.X/2
fullImage := b.Max.X

for y := 0; y < b.Max.Y; y++ {
      go func() {
        for x := 0; x < halfImage; x++ {
          oldPixel := img.At(x, y)
          r, g, b, _ := oldPixel.RGBA()
        channel1 <- rgbtogray(r, g, b)
        pixel := color.Gray{uint8(lum / 256)}
        imgSet.Set(x, y, pixel)
      }
      }()

      go func() {
        for x := halfImage; x< fullImage; x++ {
          oldPixel := img.At(x, y)
          r, g, b, _ := oldPixel.RGBA()
        channel2 <- rgbtogray(r, g, b)
        pixel := color.Gray{uint8(lum / 256)}
        imgSet.Set(x, y, pixel)
      }
      }()


}

    outFile, err := os.Create("changed.jpg")
    if err != nil {
      log.Fatal(err)
    }
    defer outFile.Close()
    jpeg.Encode(outFile, imgSet, nil)

}

This runs but just returns a black image. 这会运行,但只会返回黑色图像。 I know the way I'm going about it is wrong but I'm not 100% what route I need to be taking. 我知道我的处理方式是错误的,但我不是100%需要采取的路线。

My idea is to split the image down the middle, have one channel work on the pixels on the left and one channel work on the pixels on the right. 我的想法是将图像拆分到中间,在左边的像素上有一个通道,在右边的像素上有一个通道。 Before moving down to the next y coordinate and so on. 向下移动到下一个y坐标,依此类推。

I've tried moving all of the code in my go functions into my rgbatogray function but I was getting multiple errors to do with passing through variables etc. Would it be best to create another function which deals with the splitting etc as I think I calling my go functions should just look something like: 我试过将go函数中的所有代码移到rgbatogray函数中,但是与传递变量等有关,我遇到了多个错误。最好创建另一个处理拆分等的函数,就像我认为的那样我的go函数应该看起来像这样:

go func() {
      channel1 <- rgbtogray(r, g, b)
}()
go func() {
      channel2 <- rgbtogray(r, g, b)
}()

I'm unsure what steps I should be taking next on this so any tips and help greatly appreciated. 我不确定接下来应该采取什么步骤,因此非常感谢您提供任何提示和帮助。

Here's an implementation of @JimB's suggestion in comments. 这是@JimB建议中的注释的实现。 It exploits the fact of JPEG images being in YCbCr to process the image setting inplace Cb and Cr components to 128 using one goroutine for each of them. 它利用JPEG图像位于YCbCr中这一事实,使用一个goroutine为每个图像处理将Cb和Cr分量设置为128的图像。

func set(wg *sync.WaitGroup, a []uint8, v uint8) {
    for i := 0; i < len(a); i++ {
        a[i] = v
    }
    wg.Done()
}

func gray(i *image.YCbCr) {
    var wg sync.WaitGroup
    wg.Add(2)
    go set(&wg, i.Cb, 128)
    go set(&wg, i.Cr, 128)
    wg.Wait()
}

func main() {
    i, err := jpeg.Decode(os.Stdin)
    if err != nil {
        log.Fatalf("decoding image: %v", err)
    }
    gray(i.(*image.YCbCr))
    err = jpeg.Encode(os.Stdout, i, nil)
    if err != nil {
        log.Fatalf("encoding image: %v", err)
    }
}

It turned out pretty simple. 原来很简单。

Of course it could be extended to create more goroutines (possibly one per available core) assigning slices of Cb & Cr arrays to each for processing. 当然,可以扩展它以创建更多的goroutine(可能每个可用核一个),将Cb和Cr阵列的切片分配给每个例程进行处理。

How about creating a pipeline of pixels read from the image, converted to gray and finally set to a new image, where all steps run concurrently and each step could be internally parallelized? 如何创建从图像读取的像素流水线,转换为灰度并最终设置为新图像,其中所有步骤同时运行,并且每个步骤都可以在内部并行化?

Then Go's runtime will parallelize the tasks using all available cores. 然后,Go的运行时将使用所有可用的内核并行化任务。

This implementation works: 此实现有效:

package main

import (
    "image"
    "image/color"
    "image/jpeg"
    "log"
    "os"
    "sync"
)

type Setter interface {
    Set(x, y int, c color.Color)
}

type Pixel struct {
    X, Y int
    C    color.Color
}

func sendPixels(in image.Image, out chan Pixel) {
    b := in.Bounds()
    for x := 0; x < b.Max.X; x++ {
        for y := 0; y < b.Max.Y; y++ {
            out <- Pixel{x, y, in.At(x, y)}
        }
    }
    close(out)
}

func convertToGray(in chan Pixel, out chan Pixel) {
    var wg sync.WaitGroup
    for p := range in {
        wg.Add(1)
        go func(p Pixel) {
            r, g, b, _ := p.C.RGBA()
            l := 0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)
            out <- Pixel{p.X, p.Y, color.Gray{uint8(l / 256)}}
            wg.Done()
        }(p)
    }
    wg.Wait()
    close(out)
}

func buildImage(in chan Pixel, out Setter, done chan int) {
    for p := range in {
        out.Set(p.X, p.Y, p.C)
    }
    close(done)
}

func main() {
    i, err := jpeg.Decode(os.Stdin)
    if err != nil {
        log.Fatalf("decoding image: %v", err)
    }

    a := make(chan Pixel, 1000)
    go sendPixels(i, a)

    b := make(chan Pixel, 1000)
    go convertToGray(a, b)

    c := image.NewRGBA(i.Bounds())
    d := make(chan int)
    go buildImage(b, c, d)

    <-d

    err = jpeg.Encode(os.Stdout, c, nil)
    if err != nil {
        log.Fatalf("encoding image: %v", err)
    }
}

Which can be tested running: 可以测试哪个运行:

go run main.go <in.jpg >out.jpg

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM