down_load1117
down_load1117
2017-03-02 18:31

RGBA到并行Golang中的灰度

已采纳

I have written a program which converts RGBA image to Grayscale sequentially. 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
", "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.

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.

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 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.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • ds342222222 ds342222222 4年前

    Here's an implementation of @JimB's suggestion in comments. 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.

    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.

    点赞 评论 复制链接分享
  • dplo59755 dplo59755 4年前

    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.

    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
    
    点赞 评论 复制链接分享

为你推荐