我编写了一个程序,可以将RGBA图像依次转换为灰度图像。我现在正试图将其转换为并行运行。
我有点理解我需要这样做,但我很难开始。
这是我到目前为止所拥有的。
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)
}
这会运行但只会返回黑色图像。我知道我这样做的方式是错的,但我不是100%我需要走的路线。
我的想法是将图像分割到中间,让一个通道在左边的像素上工作,一个通道在右边的像素上工作。在向下移动到下一个y坐标之前等等。
我已经尝试将go函数中的所有代码移动到我的rgbatogray函数中但是我遇到了多个错误与传递变量等有关。最好是创建另一个处理拆分等的函数因为我认为我调用我的go函数应该看起来像:
go func() {
channel1 <- rgbtogray(r, g, b)
}()
go func() {
channel2 <- rgbtogray(r, g, b)
}()
我不确定接下来我应该采取什么步骤,所以任何提示和帮助都会非常感激。
答案 0 :(得分:2)
以下是@ JimB在评论中提出的建议。它利用YCbCr中JPEG图像的事实来处理图像设置,使用一个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)
}
}
结果很简单。
当然可以扩展为创建更多goroutine(可能每个可用核心一个)分配Cb&amp;的切片。每个Cr阵列都要进行处理。
答案 1 :(得分:0)
如何创建从图像读取的像素管道,转换为灰色并最终设置为新图像,其中所有步骤同时运行并且每个步骤可以在内部并行化?
然后Go的运行时将使用所有可用内核并行化任务。
此实施有效:
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)
}
}
哪个可以运行测试:
go run main.go <in.jpg >out.jpg