Image RGBA到灰度平行Golang
我写了一个程序,将RGBA图像按顺序转换为灰度。我现在正试图转换它,使其并行运行 我有点明白我需要怎么做,但我正在努力开始 这是我到目前为止所拥有的Image RGBA到灰度平行Golang,image,go,parallel-processing,image-manipulation,Image,Go,Parallel Processing,Image Manipulation,我写了一个程序,将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,
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)
}
主程序包
进口(
“图像”
“图像/颜色”
“图像/jpeg”
“日志”
“操作系统”
)
第64号变种
类型图像集接口{
集合(x,y int,c color.color)
}
func rgbtogray(r uint32、g uint32、b uint32)浮动64{
lum=0.299*float64(r)+0.587*float64(g)+0.114*float64(b)
回程流光
}
func main(){
文件,err:=os.Open(“flower.jpg”)
如果错误!=零{
log.Fatal(错误)
}
延迟文件。关闭()
img,err:=jpeg.Decode(文件)
如果错误!=零{
log.Fatal(os.Stderr,“%s:%v\n”,“flower.jpg”,错误)
}
通道1:=制造(64)
通道2:=制造(64)
b:=img.Bounds()
imgSet:=image.NewRGBA(b)
半图像:=b.Max.X/2
fullImage:=b.Max.X
对于y:=0;y channel1如何创建一个从图像读取的像素管道,将其转换为灰色并最终设置为新图像,其中所有步骤同时运行,每个步骤可以在内部并行化
然后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)
}
}
主程序包
进口(
“图像”
“图像/颜色”
“图像/jpeg”
“日志”
“操作系统”
“同步”
)
类型设置器接口{
集合(x,y int,c color.color)
}
类型像素结构{
十、 Y整数
颜色
}
func sendPixels(图像内、图像外像素){
b:=in.Bounds()
对于x:=0;x out这里是@JimB在评论中建议的一个实现。它利用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)
}
}
func集(wg*sync.WaitGroup,a[]uint8,v uint8){
对于i:=0;i
结果很简单
当然,它可以扩展以创建更多的goroutine(可能每个可用的核心一个)将Cb和Cr阵列的片分配给每个阵列进行处理。首先使用竞赛检测器运行您的程序。首先,您在多个goroutine中共享一个全局lum
变量,关闭goroutine中for循环中的y
值,您不从任何一个通道读取,因此发送s永远无法完成(不确定他们会去哪里),您不必等待任何goroutines完成,然后再尝试写入文件,您可以使用全局lum
调用Set,它可以是任何时间的任何值,并且您将与它们进行数据竞争,所有人都试图调用imgSet.Set
。嗨,所以第一步是使lum成为局部变量?我尝试移动for将x循环到我的rgbtogray函数中,但变量把我搞糊涂了。这是正确的方法吗?这里不需要通道:只需按逻辑分割图像,让一个goroutine在图像的左半部分工作,另一个goroutine在图像的右半部分工作。@benjano:您可能必须直接对像素数据ra进行操作而不是通过At
和Set
方法来通过竞争检测器。好的新方法是绕过接口抽象可能会获得与增加并行性一样多的性能提升。虽然这在技术上可行,但速度会慢很多数量级,并且可能会使用大量内存y、 @JimB WRT to performance:我认为这个实现是一个概念证明,可以通过将要处理的像素进行批处理来改进。WRT内存使用:你指的是什么?这会为每个像素启动一个goroutine,只有像素数作为并发goroutine数目的限制。什么是原始的数学运算对3个值的操作现在是2k堆栈加上waitgroup和channel同步。我只是在一个相对较小的映像上运行了这段代码,没有进行任何修改,它花费了32秒来完成本应花费几毫秒的操作。哦,至于性能,是的,对像素进行批处理和/或只有一定数量的工作人员使用它们将有助于Ficanly。尽管如此小的操作仍然需要大量的同步。我怀疑这条管道是否能比N个工人简单地对原始像素数据进行单独切片并进行适当修改更好。@JimB你总是对的。我已经添加了另一个答案,显示了你在上面的评论中建议的实现。您好,感谢您的帮助回答,我现在正在尝试让它运行,我正在尝试使用我的os.Open(“flower.jpg”)代码让它仍然工作,因为这是我理解图像加载的方式。这个代码与该方法一起工作吗?@benjano,是的,在调用jpeg.Decode()时使用Open文件而不是os.Stdin.嗨,为了让我更好地理解这是如何工作的,两个并行的围棋程序是围棋集(&wg,i.Cb,128。这并不是将图像一分为二,即从中间向下分割。它是由一个处理色度蓝的函数和一个处理色度红的函数来实现的。我理解这一点了吗correctly@benjano您可能想查看我的一个类似的问题,该问题使用工作程序的有限集合。嗨,谢谢
go run main.go <in.jpg >out.jpg
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)
}
}