英文:
How to reduce the effect of pixelation by computing the color value at several points within each pixel and taking the average?
问题
这是《The Go Programming Language》一书中的一个练习,题目是实现超采样技术来减少像素化效果。超采样是通过在每个像素内计算多个点的颜色值并取平均值来实现的。最简单的方法是将每个像素分成四个“子像素”。以下是你的解决方案:
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main
import (
"image"
"image/color"
"image/png"
"math/cmplx"
"os"
)
func main() {
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
swidth, sheight = width * 2, height * 2
)
var superColors [swidth][sheight]color.Color
for py := 0; py < sheight; py++ {
y := float64(py) / sheight * (ymax - ymin) + ymin
for px := 0; px < swidth; px++ {
x := float64(px) / swidth * (xmax - xmin) + xmin
z := complex(x, y)
superColors[px][py] = mandelbrot(z)
}
}
img := image.NewRGBA(image.Rect(0, 0, width, height))
for j := 0; j < height; j++ {
for i := 0; i < width; i++ {
si, sj := 2*i, 2*j
r1, g1, b1, a1 := superColors[si][sj].RGBA()
r2, g2, b2, a2 := superColors[si+1][sj].RGBA()
r3, g3, b3, a3 := superColors[si+1][sj+1].RGBA()
r4, g4, b4, a4 := superColors[si][sj+1].RGBA()
avgColor := color.RGBA{
uint8((r1 + r2 + r3 + r4) / 4),
uint8((g1 + g2 + g3 + g4) / 4),
uint8((b1 + b2 + b3 + b4) / 4),
uint8((a1 + a2 + a3 + a4) / 4)}
img.Set(i, j, avgColor)
}
}
png.Encode(os.Stdout, img)
}
func mandelbrot(z complex128) color.Color {
const iterations = 200
const contrast = 15
var v complex128
for n := uint8(0); n < iterations; n++ {
v = v*v + z
if cmplx.Abs(v) > 2 {
return color.Gray{255 - contrast*n}
}
}
return color.Black
}
然而,你的解决方案似乎没有减少像素化效果。你的解决方案是否有问题?
英文:
This is an exercise from The Go Programming Language by Donovan & Kernighan:
> Exercise 3.6: Supersampling is a technique to reduce the effect of pixelation by computing the color value at several points within each pixel and taking the average. The simplest method is to divide each pixel into four "subpixels". Implement it.
Here is my solution:
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main
import (
//"fmt"
"image"
"image/color"
"image/png"
"math/cmplx"
"os"
)
func main() {
const (
xmin, ymin, xmax, ymax = -2, -2, +2, +2
width, height = 1024, 1024
swidth, sheight = width * 2, height * 2
)
var superColors [swidth][sheight]color.Color
for py := 0; py < sheight; py++ {
y := float64(py) / sheight * (ymax - ymin) + ymin
for px := 0; px < swidth; px++ {
x := float64(px) / swidth * (xmax - xmin) + xmin
z := complex(x, y)
superColors[px][py] = mandelbrot(z)
}
}
img := image.NewRGBA(image.Rect(0, 0, width, height))
for j := 0; j < height; j++ {
for i := 0; i < width; i++ {
si, sj := 2*i, 2*j
r1, g1, b1, a1 := superColors[si][sj].RGBA()
r2, g2, b2, a2 := superColors[si+1][sj].RGBA()
r3, g3, b3, a3 := superColors[si+1][sj+1].RGBA()
r4, g4, b4, a4 := superColors[si][sj+1].RGBA()
avgColor := color.RGBA{
uint8((r1 + r2 + r3 + r4) / 4),
uint8((g1 + g2 + g3 + g4) / 4),
uint8((b1 + b2 + b3 + b4) / 4),
uint8((a1 + a2 + a3 + a4) / 4)}
img.Set(i, j, avgColor)
}
}
png.Encode(os.Stdout, img)
}
func mandelbrot(z complex128) color.Color {
const iterations = 200
const contrast = 15
var v complex128
for n := uint8(0); n < iterations; n++ {
v = v*v + z
if cmplx.Abs(v) > 2 {
return color.Gray{255 - contrast*n}
}
}
return color.Black
}
However, the result of my solution seems that it doesn't reduce the effect of pixelation:
Is my solution wrong?
答案1
得分: 2
在go
语言中,当你通过Color.RGBA()
获取RGBA值时,每个颜色分量(R
,G
,B
,A
)都用16位无符号表示,因此范围在0-0xffff
(0-65535)之间。然而,当你将图像保存为PNG格式时,每个分量的范围在0-0xff
(0-255)之间。你需要使用以下公式正确缩放每个颜色分量:
//例如,红色分量
r := ((r1+r2+r3+r4)/4)*(255/65535) => (r1+r2+r3+r4)/1028
在你的情况下,正确的公式是:
avgColor := color.RGBA{
uint8((r1 + r2 + r3 + r4) / 1028),
uint8((g1 + g2 + g3 + g4) / 1028),
uint8((b1 + b2 + b3 + b4) / 1028),
uint8((a1 + a2 + a3 + a4) / 1028)}
英文:
In go
, when you obtained the RGBA values through Color.RGBA()
, each color component (R
, G
, B
, A
) is represented by 16-bit unsigned, thus the range is between 0-0xffff
(0-65535). However, when you save the image to PNG, each component is between 0-0xff
(0-255). You need to correctly scale down each color component using the following formula:
//e.g. red component
r := ((r1+r2+r3+r4)/4)*(255/65535) => (r1+r2+r3+r4)/1028
In your case, the correct formula is:
avgColor := color.RGBA{
uint8((r1 + r2 + r3 + r4) / 1028),
uint8((g1 + g2 + g3 + g4) / 1028),
uint8((b1 + b2 + b3 + b4) / 1028),
uint8((a1 + a2 + a3 + a4) / 1028)}
答案2
得分: 0
在这种情况下,你可以通过执行右移操作将uint32类型的值转换为uint8类型:
r := r >> 8
这将加快计算速度。
另请参考:
https://stackoverflow.com/questions/35374300/why-does-golang-rgba-rgba-method-use-and
英文:
You can convert values from uint32 to uint8 in this case by performing right shift operation:
r := r >> 8
This will speed up computation time.
See also:
https://stackoverflow.com/questions/35374300/why-does-golang-rgba-rgba-method-use-and
答案3
得分: 0
如果你将两个uint8相加,会导致内存溢出。例如:
var a, b uint8 = 200, 100
fmt.Printf(a+b) // 结果为44 (300-256)
在将它们相加之前,将你的uint8转换为int:
var a, b uint8 = 200, 100
fmt.Printf(int(a)+int(b)) // 结果为300
在你的情况下:
var a, b uint8 = 200, 100
fmt.Printf(uint8((int(a)+int(b)/2)) // 结果为150
英文:
If you add two uint8, it's overflow the memory. For example :
var a, b uint8 = 200 , 100
fmt.Printf(a+b) // result = 44 (300-256)
Just convert your uint8 to int before to add them together :
var a, b uint8 = 200 , 100
fmt.Printf(int(a)+int(b)) // result = 300
and in your case :
var a, b uint8 = 200 , 100
fmt.Printf(uint8((int(a)+int(b)/2)) // result = 150
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论