Gaussian Blur implementation generates weird output - go

I'm trying to implement a Gaussian Blur on golang image.Image objects. For the following image:
The output image generated is:
As one can see, the output image contains some unprocessed borders that corresponds to the current implementation decision to not process the edges, which leads me to think that I might have messed up on calculations somehow (what I mean is, this part of the implementation works, so I can discard off-by-one errors while iterating through image pixels). I've reviewed this code many times, but I can't find my mistake. I would really appreciate some help and considerations on the implementation, that could help me solve the problem. The code is contained below. If any edits or clarifications are necessary, please let me know!
package main
import (
"image"
"image/color"
"image/draw"
"image/jpeg"
"math"
"os"
)
func main() {
f, err := os.Open("dog.jpeg")
if err != nil {
panic(err)
}
img, err := jpeg.Decode(f)
if err != nil {
panic(err)
}
newImg := gaussianBlur(img, 3)
out, err := os.Create("dog-blurred.jpeg")
if err != nil {
panic(err)
}
err = jpeg.Encode(out, newImg, nil)
if err != nil {
panic(err)
}
}
func applyGaussianFunction(x, y, stdDev float64) float64 {
// eFactor := 1 / (2 * math.Pi * stdDev*stdDev);
ePowNominator := -(x*x + y*y);
ePowDenominator := 2 * stdDev*stdDev;
return math.Pow(math.E, (ePowNominator/ePowDenominator));
}
func generateKernel(radius int) [][]float64 {
size := 1 + (radius * 2);
kernel := make([][]float64, size);
stdDev := math.Max(float64(radius / 2), 1);
sum := float64(0);
for i := 0; i < size; i++ {
kernel[i] = make([]float64, size);
}
for i := -radius; i < radius + 1; i++ {
for j := -radius; j < radius + 1; j++ {
val := applyGaussianFunction(float64(j), float64(i), stdDev);
kernel[i + radius][j + radius] = val;
sum += val;
}
}
for i := 0; i < size; i++ {
for j := 0; j < size; j++ {
kernel[i][j] /= sum;
}
}
return kernel;
}
func makeImageRGBA(src image.Image) *image.RGBA {
b := src.Bounds().Size();
rgba := image.NewRGBA(image.Rect(0, 0, b.X, b.Y));
draw.Draw(rgba, rgba.Bounds(), src, image.Pt(0, 0), draw.Src);
return rgba;
}
func gaussianBlur(img image.Image, radius int) image.Image {
size := img.Bounds().Size();
rgbaImg := image.NewRGBA(image.Rect(0, 0, size.X, size.Y));
kernel := generateKernel(radius);
for y := radius; y < size.Y - radius; y++ {
for x := radius; x < size.X - radius; x++ {
var nr, ng, nb, na float64 = 0, 0, 0, 0;
for i := -radius; i < radius + 1; i++ {
for j := -radius; j < radius + 1; j++ {
// NEW: Get pixels from original Image
pr, pg, pb, pa := img.At(x - j, y - i).RGBA();
nr += float64(pr) * kernel[i + radius][j + radius];
ng += float64(pg) * kernel[i + radius][j + radius];
nb += float64(pb) * kernel[i + radius][j + radius];
na += float64(pa) * kernel[i + radius][j + radius];
}
}
// Handle overflow by using 64-bit alphapremultiplied values
rgbaImg.Set(x, y, color.RGBA64{uint16(nr), uint16(ng), uint16(nb), uint16(na)});
}
}
return rgbaImg;
}
EDITS
I modified the code so that pixels are read from the original image, not from rgbaImg
I've also commented eFactor from the applyGaussianFunction function, since I'm already normalizing the kernel with the sum variable
Modified .Set method to use 64-bit RGBA struct
This is the newly generated image
Those black borders are easy to solve, I'm already working them out. This is not a part of the problem anymore.

You're reading from the same image that you're writing to. You shall read from the original image instead:
pr, pg, pb, pa := img.At(x+j, y+i).RGBA()
EDIT:
Additionally, Image.At returns color.RGBA, and func (color.RGBA) RGBA returns colors in the 0 to 0xFFFF range. However color.RGBA constructor expects them to be in 0 to 255 range. You may want to use color.RGBA64 when writing the result:
rgbaImg.Set(x, y, color.RGBA64{uint16(nr), uint16(ng), uint16(nb), uint16(na)});

Related

How to implement Custom cropping using golang

Am looking to do custom cropping on a set of images, Instead of cropping normally, using height and width i want the flexibility of getting an output image that is cropped like a polygon or an hexagon for example, Am using the library github.com/fogleman/gg, and the built in module "image", and github.com/disintegration/imaging, but I didn't find a way to customize the cropping, i also looked for an online SaaS to do this, like imgix or imageresizer.io, but they don't seem to offer that, i know golang is the right language for this maybe i didn't look hard enough, please Help
my sample Code looks like:
var image image.Image
dc := NewContext(1000, 1000)
image = imaging.Fill(profile, 800, 750, imaging.Center, imaging.Lanczos)
// Cropping needs to happen here
dc.DrawImage(image, 123, 250)
A bit longer than expected but here you have PNG image cropping with transparent background to a rectangle. You can modify the code for different shapes by changing the getPixAlpha function.
Just add the package name and it should include the imports, then add an image test.png and it should create a test-output.png
Note: You may want to make some minor modifications for using it as a service.
type Pixel struct {
R int
G int
B int
A int
}
func LogPanic(err error, msg string) {
if err != nil {
log.Printf("ERROR: %v %s", err, msg)
panic(err)
}
}
func getPixAlpha(x, y, halfWidth int) int {
if x < halfWidth-y || x > halfWidth+y {
return 0
}
if y > halfWidth+x {
return 0
}
if x > halfWidth*3-y && y > halfWidth*3-x {
return 0
}
return int(255)
}
func getPixels(file io.Reader) ([][]Pixel, error) {
img, _, err := image.Decode(file)
LogPanic(err, "error reading image")
bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
var pixels [][]Pixel
for x := 0; x < width; x++ {
var row []Pixel
for y := 0; y < height; y++ {
row = append(row, rgbaToPixel(img.At(x, y).RGBA()))
}
pixels = append(pixels, row)
}
return pixels, nil
}
func rgbaToPixel(r uint32, g uint32, b uint32, a uint32) Pixel {
return Pixel{int(r / 257), int(g / 257), int(b / 257), int(a / 257)}
}
func getRgbaPic(pixels [][]Pixel) [][]Pixel {
dx := len(pixels)
dy := len(pixels[0])
for x := 0; x < dx; x++ {
for y := 0; y < dy; y++ {
pixels[x][y].A = getPixAlpha(x, y, len(pixels)/2)
}
}
return pixels
}
func main() {
file, err := os.Open("./test.png")
LogPanic(err, "Error opening file")
defer file.Close()
pixels, err := getPixels(file)
LogPanic(err, "Error reading image")
pixels = getRgbaPic(pixels)
img := image.NewRGBA(image.Rect(0, 0, len(pixels), len(pixels[0])))
for x := 0; x < len(pixels); x++ {
for y := 0; y < len(pixels[0]); y++ {
img.Set(x, y, color.RGBA{
uint8(pixels[x][y].R),
uint8(pixels[x][y].G),
uint8(pixels[x][y].B),
uint8(pixels[x][y].A),
})
}
}
buf := &bytes.Buffer{}
err = png.Encode(buf, img)
LogPanic(err, "Error encoding")
err = ioutil.WriteFile("test-output.png", buf.Bytes(), 0666)
LogPanic(err, "Error writing file")
}

Implementing a gradient descent

I'm trying to implement a gradient descent in Go. My goal is to predict the cost of a car from it's mileage.
Here is my data set:
km,price
240000,3650
139800,3800
150500,4400
185530,4450
176000,5250
114800,5350
166800,5800
89000,5990
144500,5999
84000,6200
82029,6390
63060,6390
74000,6600
97500,6800
67000,6800
76025,6900
48235,6900
93000,6990
60949,7490
65674,7555
54000,7990
68500,7990
22899,7990
61789,8290
I've tried various approaches, like normalizing the data set, not normalizing it, leaving thetas as is, denormalizing thetas... But I cannot get the correct result.
My maths must be off somewhere, but I cannot figure out where.
The result I'm trying to get should be approximately t0 = 8500, t1 = -0.02
My implementation is the following:
package main
import (
"encoding/csv"
"fmt"
"log"
"math"
"os"
"strconv"
)
const (
dataFile = "data.csv"
iterations = 20000
learningRate = 0.1
)
type dataSet [][]float64
var minKm, maxKm, minPrice, maxPrice float64
func (d dataSet) getExtremes(column int) (float64, float64) {
min := math.Inf(1)
max := math.Inf(-1)
for _, row := range d {
item := row[column]
if item > max {
max = item
}
if item < min {
min = item
}
}
return min, max
}
func normalizeItem(item, min, max float64) float64 {
return (item - min) / (max - min)
}
func (d *dataSet) normalize() {
minKm, maxKm = d.getExtremes(0)
minPrice, maxPrice = d.getExtremes(1)
for _, row := range *d {
row[0], row[1] = normalizeItem(row[0], minKm, maxKm), normalizeItem(row[1], minPrice, maxPrice)
}
}
func processEntry(entry []string) []float64 {
if len(entry) != 2 {
log.Fatalln("expected two fields")
}
km, err := strconv.ParseFloat(entry[0], 64)
if err != nil {
log.Fatalln(err)
}
price, err := strconv.ParseFloat(entry[1], 64)
if err != nil {
log.Fatalln(err)
}
return []float64{km, price}
}
func getData() dataSet {
file, err := os.Open(dataFile)
if err != nil {
log.Fatalln(err)
}
reader := csv.NewReader(file)
entries, err := reader.ReadAll()
if err != nil {
log.Fatalln(err)
}
entries = entries[1:]
data := make(dataSet, len(entries))
for k, entry := range entries {
data[k] = processEntry(entry)
}
return data
}
func outputResult(theta0, theta1 float64) {
file, err := os.OpenFile("weights.csv", os.O_WRONLY, 0644)
if err != nil {
log.Fatalln(err)
}
defer file.Close()
file.Truncate(0)
file.Seek(0, 0)
file.WriteString(fmt.Sprintf("theta0,%.6f\ntheta1,%.6f\n", theta0, theta1))
}
func estimatePrice(theta0, theta1, mileage float64) float64 {
return theta0 + theta1*mileage
}
func (d dataSet) computeThetas(theta0, theta1 float64) (float64, float64) {
dataSize := float64(len(d))
t0sum, t1sum := 0.0, 0.0
for _, it := range d {
mileage := it[0]
price := it[1]
err := estimatePrice(theta0, theta1, mileage) - price
t0sum += err
t1sum += err * mileage
}
return theta0 - (t0sum / dataSize * learningRate), theta1 - (t1sum / dataSize * learningRate)
}
func denormalize(theta, min, max float64) float64 {
return theta*(max-min) + min
}
func main() {
data := getData()
data.normalize()
theta0, theta1 := 0.0, 0.0
for k := 0; k < iterations; k++ {
theta0, theta1 = data.computeThetas(theta0, theta1)
}
theta0 = denormalize(theta0, minKm, maxKm)
theta1 = denormalize(theta1, minPrice, maxPrice)
outputResult(theta0, theta1)
}
What should I fix in order to properly implement a gradient descent?
Linear Regression is really simple:
// yi = alpha + beta*xi + ei
func linearRegression(x, y []float64) (float64, float64) {
EX := expected(x)
EY := expected(y)
EXY := expectedXY(x, y)
EXX := expectedXY(x, x)
covariance := EXY - EX*EY
variance := EXX - EX*EX
beta := covariance / variance
alpha := EY - beta*EX
return alpha, beta
}
Try it here, Output:
8499.599649933218 -0.021448963591702314 396270.87871142407
Code:
package main
import (
"encoding/csv"
"fmt"
"strconv"
"strings"
)
func main() {
x, y := readXY(`data.csv`)
alpha, beta := linearRegression(x, y)
fmt.Println(alpha, beta, -alpha/beta) // 8499.599649933218 -0.021448963591702314 396270.87871142407
}
// https://en.wikipedia.org/wiki/Ordinary_least_squares#Simple_linear_regression_model
// yi = alpha + beta*xi + ei
func linearRegression(x, y []float64) (float64, float64) {
EX := expected(x)
EY := expected(y)
EXY := expectedXY(x, y)
EXX := expectedXY(x, x)
covariance := EXY - EX*EY
variance := EXX - EX*EX
beta := covariance / variance
alpha := EY - beta*EX
return alpha, beta
}
// E[X]
func expected(x []float64) float64 {
sum := 0.0
for _, v := range x {
sum += v
}
return sum / float64(len(x))
}
// E[XY]
func expectedXY(x, y []float64) float64 {
sum := 0.0
for i, v := range x {
sum += v * y[i]
}
return sum / float64(len(x))
}
func readXY(filename string) ([]float64, []float64) {
// file, err := os.Open(filename)
// if err != nil {
// panic(err)
// }
// defer file.Close()
file := strings.NewReader(data)
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
records = records[1:]
size := len(records)
x := make([]float64, size)
y := make([]float64, size)
for i, v := range records {
val, err := strconv.ParseFloat(v[0], 64)
if err != nil {
panic(err)
}
x[i] = val
val, err = strconv.ParseFloat(v[1], 64)
if err != nil {
panic(err)
}
y[i] = val
}
return x, y
}
var data = `km,price
240000,3650
139800,3800
150500,4400
185530,4450
176000,5250
114800,5350
166800,5800
89000,5990
144500,5999
84000,6200
82029,6390
63060,6390
74000,6600
97500,6800
67000,6800
76025,6900
48235,6900
93000,6990
60949,7490
65674,7555
54000,7990
68500,7990
22899,7990
61789,8290`
Gradient descent is based on the observation that if the multi-variable function F(x) is defined and differentiable in a neighborhood of a point a , then F(x) decreases fastest if one goes from a in the direction of the negative gradient of F at a,-∇F(a), for example:
// F(x)
f := func(x float64) float64 {
return alpha + beta*x // write your target function here
}
Derivative function:
h := 0.000001
// Derivative function ∇F(x)
df := func(x float64) float64 {
return (f(x+h) - f(x-h)) / (2 * h) // write your target function derivative here
}
Search:
minimunAt := 1.0 // We start the search here
gamma := 0.01 // Step size multiplier
precision := 0.0000001 // Desired precision of result
max := 100000 // Maximum number of iterations
currentX := 0.0
step := 0.0
for i := 0; i < max; i++ {
currentX = minimunAt
minimunAt = currentX - gamma*df(currentX)
step = minimunAt - currentX
if math.Abs(step) <= precision {
break
}
}
fmt.Printf("Minimum at %.8f value: %v\n", minimunAt, f(minimunAt))

Adding an alpha channel performance

I'm looking to convert my array to a PNG image. It is currently an RGB8 encoded image. I can do so using the following code:
s2 := make([]uint8, 2048*2448*3)
err = dset.Read(&s2)
if err != nil {
panic(err)
}
var (
width = 2048
height = 2448
rgb = 3
)
to1D := func(x, y int) int {
return (x * height * rgb) + (rgb * y)
}
img := image.NewRGBA(image.Rect(0, 0, width, height))
for ix := 0; ix < width; ix++ {
for iy := 0; iy < height; iy++ {
cords := to1D(ix, iy)
img.SetRGBA(ix, iy, color.RGBA{R: s2[cords], G: s2[cords+1], B: s2[cords+2], A: 255})
}
}
Is there a better way to add an alpha channel that doesn't involve looping through each pixel and setting it individually?
Thank you!

Multiple line plots sharing abscissas axis in gonum/plot

Is it possible to make multiple line plots with common abscissas axis in gonum/plot?
In matplotlib it would look like this.
Yes, it is possible. You can use plot.Align:
package main
import (
"math/rand"
"os"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
"gonum.org/v1/plot/vg/vgimg"
)
func main() {
rand.Seed(int64(0))
const rows, cols = 2, 1
plots := make([][]*plot.Plot, rows)
for j := 0; j < rows; j++ {
plots[j] = make([]*plot.Plot, cols)
for i := 0; i < cols; i++ {
p := randomLinePlot(rand.Intn(10))
// make sure the horizontal scales match
p.X.Min = 0
p.X.Max = 5
plots[j][i] = p
}
}
img := vgimg.New(vg.Points(150), vg.Points(175))
dc := draw.New(img)
t := draw.Tiles{
Rows: rows,
Cols: cols,
}
canvases := plot.Align(plots, t, dc)
for j := 0; j < rows; j++ {
for i := 0; i < cols; i++ {
if plots[j][i] != nil {
plots[j][i].Draw(canvases[j][i])
}
}
}
w, err := os.Create("aligned.png")
if err != nil {
panic(err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err := png.WriteTo(w); err != nil {
panic(err)
}
}
This generates the following plots in a single PNG file:
You can find another example below the GoDoc entry of plot.Align

Julia set image rendering ruined by concurrency

I have the following code that I am to change into a concurrent program.
// Stefan Nilsson 2013-02-27
// This program creates pictures of Julia sets (en.wikipedia.org/wiki/Julia_set).
package main
import (
"image"
"image/color"
"image/png"
"log"
"math/cmplx"
"os"
"strconv"
)
type ComplexFunc func(complex128) complex128
var Funcs []ComplexFunc = []ComplexFunc{
func(z complex128) complex128 { return z*z - 0.61803398875 },
func(z complex128) complex128 { return z*z + complex(0, 1) },
}
func main() {
for n, fn := range Funcs {
err := CreatePng("picture-"+strconv.Itoa(n)+".png", fn, 1024)
if err != nil {
log.Fatal(err)
}
}
}
// CreatePng creates a PNG picture file with a Julia image of size n x n.
func CreatePng(filename string, f ComplexFunc, n int) (err error) {
file, err := os.Create(filename)
if err != nil {
return
}
defer file.Close()
err = png.Encode(file, Julia(f, n))
return
}
// Julia returns an image of size n x n of the Julia set for f.
func Julia(f ComplexFunc, n int) image.Image {
bounds := image.Rect(-n/2, -n/2, n/2, n/2)
img := image.NewRGBA(bounds)
s := float64(n / 4)
for i := bounds.Min.X; i < bounds.Max.X; i++ {
for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
r := uint8(0)
g := uint8(0)
b := uint8(n % 32 * 8)
img.Set(i, j, color.RGBA{r, g, b, 255})
}
}
return img
}
// Iterate sets z_0 = z, and repeatedly computes z_n = f(z_{n-1}), n ≥ 1,
// until |z_n| > 2 or n = max and returns this n.
func Iterate(f ComplexFunc, z complex128, max int) (n int) {
for ; n < max; n++ {
if real(z)*real(z)+imag(z)*imag(z) > 4 {
break
}
z = f(z)
}
return
}
I have decided to try and make the Julia() function concurrent. So I changed it to:
func Julia(f ComplexFunc, n int) image.Image {
bounds := image.Rect(-n/2, -n/2, n/2, n/2)
img := image.NewRGBA(bounds)
s := float64(n / 4)
for i := bounds.Min.X; i < bounds.Max.X; i++ {
for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
go func(){
n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
r := uint8(0)
g := uint8(0)
b := uint8(n % 32 * 8)
img.Set(i, j, color.RGBA{r, g, b, 255})
}()
}
}
return img
This change causes the images to look very different. The patterns are essentially the same, but there are a lot of white pixels that were not there before.
What is happening here?
There are 2 problems:
You don't actually wait for your goroutines to finish.
You don't pass i and j to the goroutine, so they will almost always be the last i and j.
Your function should look something like:
func Julia(f ComplexFunc, n int) image.Image {
var wg sync.WaitGroup
bounds := image.Rect(-n/2, -n/2, n/2, n/2)
img := image.NewRGBA(bounds)
s := float64(n / 4)
for i := bounds.Min.X; i < bounds.Max.X; i++ {
for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
wg.Add(1)
go func(i, j int) {
n := Iterate(f, complex(float64(i)/s, float64(j)/s), 256)
r := uint8(0)
g := uint8(0)
b := uint8(n % 32 * 8)
img.Set(i, j, color.RGBA{r, g, b, 255})
wg.Done()
}(i, j)
}
}
wg.Wait()
return img
}
A bonus tip, when diving into concurrency, it's usually a good idea to try your code with the race detector.
You might have to use a mutex to call img.Set but I'm not very sure and I can't test atm.

Resources