Golang - Swap PNG channels of a picture using Image and Image/PNG - image

I'm trying to write a short one, which will read a PNG file, and swap one channel with the other (R,G,B) being the possible choices.
I can't find out however, how to extract the integer from the color.Color object returned by image.At(x,y) . Writing it back would probably easier with image.Set(x,y,color) once i can construct the new RGBA color with the swapped channels.
Here I am now (you can pretty much skip to the last loop):
package main
import (
"flag"
"fmt"
//"image"
"image/color"
"image/png"
"os"
)
type Choice struct {
value string
valid bool
}
func (c *Choice) validate() {
goodchoices := []string{"R", "G", "B"}
for _, v := range goodchoices {
if c.value == v {
c.valid = true
}
}
}
func main() {
var fname string
var c1 Choice
var c2 Choice
flag.StringVar(&c1.value, "c1", "", "The color channel to swap - R or G or B ")
flag.StringVar(&c2.value, "c2", "", "The color channel to swap with - R or G or B ")
flag.StringVar(&fname, "f", "", "A .png image (normal map)")
flag.Parse()
c1.validate()
c2.validate()
if c1.valid == true && c2.valid == true {
fmt.Println("We could proceed..")
fmt.Println("Swapping channels:", c1.value, "<->", c2.value, "In", fname) //for testing
} else {
fmt.Println("Invalid channel... Please use R, G or B.")
return
}
file, err := os.Open(fname)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
pic, err := png.Decode(file)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", fname, err)
return
}
b := pic.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
col := pic.At(x, y)
???? How do I swap the channels in col ????
}
}
}
I'm really new to Go and programming in general, so please consider it in your answer. Thank You.

Hmmm, that was harder than I thought it would be - I wonder if anyone can come up with a better idea!
The problem is that you don't know the concrete type that png.Decode returns - it may return any of the image types. You only have an image.Image interface which doesn't have a Set method.
To get round that, first define an interface which all the Image types which can set pixels satisfies
type ImageSet interface {
Set(x, y int, c color.Color)
}
Next see whether pic implements that interface (go will panic if it doesn't - use the picSet, ok form if that bothers you)
// Get an interface which can set pixels
picSet := pic.(ImageSet)
Now your loop looks like this - I only swapped red and green so you can see the idea.
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
col := pic.At(x, y)
r, g, b, a := col.RGBA()
// Swap green and red
newCol := color.RGBA{uint8(g>>8), uint8(r>>8), uint8(b>>8), uint8(a>>8)}
picSet.Set(x, y, newCol)
}
}
I suspect that a high performing version of this would have to use a type switch to determine which image type it was, then have a customized code for each one with uint8s for 24 bit images and uint16s for 48 bit images etc.
Here is the complete working example if you want to have a go. It doesn't work in the playground though - you'll have to download it.
Update: Just noticed your comment. If you know that you have an RGBA image, then you can use a type assertion to get the underlying image which makes things a whole lot easier.
// Get an image.RGBA if it is one
rgba, ok := pic.(*image.RGBA)
if !ok {
fmt.Println("That wasn't an RGBA!")
return
}
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
// Note type assertion to get a color.RGBA
col := rgba.At(x, y).(color.RGBA)
// Swap green and red
col.G, col.R = col.R, col.G
rgba.Set(x, y, col)
}
}

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")
}

Can't trace the problem in BST delete function

I can't trace the mistake in my logic in BST delete function in Go.
func delete(d *Node, v int) {
if d == nil {
fmt.Println("The tree is empty")
}
if v < d.key {
delete(d.left, v)
} else if v > d.key {
delete(d.right, v)
} else if v == d.key {
if d.right == nil && d.left == nil {
d = nil
} else {
if d.left == nil && d.right != nil {
d.key= d.right.key
delete(d.right,d.key)
} else if d.right == nil && d.left != nil {
d.key= d.left.key
delete(d.left,d.key)
}else{
min := minvalue(d.right)
d.key = min.key
delete(d.right, min.key)
}
}
}
}
The output shouldn't contain 4 but the result is instead showing 6 two times
The expected output is 5 6, but it's showing 4 6 6
As several have noted in the comments, you should provide a Minimal Reproducible Example, which helps people avoid wasting time on simple misunderstandings.
In this case, though, it's pretty obvious what you have done wrong—at least, at the first layer. (There may be more, depending on what you intend to do with these trees.)
Consider the following function:
func setToNil(p *int) {
p = nil
}
Let's use this from a main:
func main() {
x := 3
px := &x
fmt.Println("before: x =", x, "px =", px)
setToNil(px)
fmt.Println("after: x =", x, "px =", px)
}
(Complete version on the Go playground)
What do you expect this program to do? Try it out: did it do what you expected? Why, or why not? If not, what about this variant:
func setToTheAnswer(i int) {
i = 42
}
func main() {
x := 3
fmt.Println("before: x =", x)
setToTheAnswer(x)
fmt.Println("after: x =", x)
}
Fill in the rest and try it out. Why didn't x change? (Should it have changed? If you think so, why do you think so? The language definition says that it should not.)
Now, compare that to this version:
func setToTheAnswer(p *int) {
*p = 42
}
func setToNil(q **int) {
*q = nil
}
func main() {
x := 3
px := &x
fmt.Println("before: x =", x, "px =", px)
setToTheAnswer(px)
setToNil(&px) // note the & in front of px
fmt.Println("after: x =", x, "px =", px)
}
What will this version do? Try it on the playground.
With that in mind, think about your variable d
Your function:
func delete(d *Node, v int) {
// ...
}
takes a parameter named d of type pointer to Node (and v of type int of course). If you change d in delete, that has no effect on any * Node variable in any caller, because d is a copy of this pointer-to-Node. You can change *d to change the Node to which the caller's pointer points, but you cannot change the caller's pointer.
There are multiple different ways to fix this. For instance, instead of taking a d *Node you might take a different object that contains a root *Node pointer, or you might take a pd **Node so that you can update a d *Node in the caller. Which is the right way? That's up to you.

How to fill a slice with scan values

I'm brand new to Go and having trouble getting fmt.scan() to fill a slice. The number of input values is dynamic and I can't use a for loop. My initial thought was to try this:
package main
import "fmt"
func main() {
var x []int
fmt.Println("Enter input")
fmt.Scanf("%v", append(x))
fmt.Println(x)
}
Which obviously doesn't work. Can someone point me in the right direction?
[Get] fmt.Scan() to fill a slice. The number of input values is dynamic and I can't use a for loop.
Perhaps, something like this:
package main
import "fmt"
func input(x []int, err error) []int {
if err != nil {
return x
}
var d int
n, err := fmt.Scanf("%d", &d)
if n == 1 {
x = append(x, d)
}
return input(x, err)
}
func main() {
fmt.Println("Enter input:")
x := input([]int{}, nil)
fmt.Println("Input:", x)
}
Output:
Enter input:
1
2 3
4
5 6 7
Input: [1 2 3 4 5 6 7]
ADDENDUM:
When storage is allocated for a variable or a new value is created, and no explicit initialization is provided, the variable or value is given a default value, the zero value for its type: nil for slices. Conversions are expressions of the form T(x) where T is a type and x is an expression that can be converted to type T. []int(nil) is a conversion to the zero value for the slice value []int.
x := input([]int(nil), nil)
is equivalent to
x := input([]int{}, nil)
or
var x []int
x = input(x, nil)
I have revised my answer to use:
x := input([]int{}, nil)
I'm new to Go, so this are my 2cents as a newbie.
func main(){
var numsToInput int
fmt.Println("Welcome user!")
fmt.Println("How many numbers would you like to scale today?")
fmt.Scan(&numsToInput)
fmt.Println("Type please the ", num, " numbers: ")
var values []float32 // Empty slice
for i := 0; i < num; i++{
var val float32
fmt.Scanln(&val)
values = append(values, val)
}
fmt.Println(values)
}
It's not a very elaborate program, but certainly it's simple.
I hope it was useful.
Using simple packages and more logic, you could try this,
package main
import "fmt"
func main() {
var ele rune
var size int
var sli = make([]int,0,1)
size = cap(sli)
for i:=0; i<=size; i++{
if i>=len(sli){
size=size+1
}
ele = 0
fmt.Println("Enter a number to add: ")
fmt.Scan(&ele)
if ele==0 {
fmt.Println("Stopping!")
break
}
sli = append(sli, int(ele))
}
fmt.Println(sli)
}
The code would stop and print the slice when you enter anything other than an integer.

How to open a image in Go to get binary data of black and white pixels?

I've been trying for sometime to open an image in binary mode with Go. In Python I'd use the Pillow and image.open() (rb mode). Example.
img = Image.open("PNG.png")
pix = img.getdata() #where 0 is black and 1 is white pixel
That would open the image with very clean binary of white and black dots like the image below. In go I've tried os.Open(file.jpg) to open the file.. I've tried decoding it with image.Decode(), I've loaded the file into bytes.Buffer, I've tried fmt.Sprintf("%b", data), all of the solutions give a byte array. Converting that byte array to binary looks nothing like the image above. I've also tried encoding/binary and its the same story with just getting bytes and the binary generated isn't what i want...
Most recently I've tried this
package main
import (
"fmt"
"image"
"image/jpeg"
"io"
"log"
"os"
)
// Pixel struct example
type Pixel struct {
R int
G int
B int
A int
}
func main() {
// You can register another format here
image.RegisterFormat("jpg", "jpg", jpeg.Decode, jpeg.DecodeConfig)
file, err := os.Open("/Users/marcsantiago/Desktop/2033bb1b194adace86f99c7bb7d72e81.jpg")
if err != nil {
log.Fatalln("Error: File could not be opened")
}
defer file.Close()
pixels, err := getPixels(file)
if err != nil {
log.Fatalln("Error: Image could not be decoded")
}
black := Pixel{0, 0, 0, 255}
for i := range pixels {
if pixels[i] == black {
fmt.Print("0")
} else {
fmt.Print("1")
}
}
}
func getPixels(file io.Reader) ([]Pixel, error) {
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
var pixels []Pixel
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
pixels = append(pixels, rgbaToPixel(img.At(x, y).RGBA()))
}
}
return pixels, nil
}
// img.At(x, y).RGBA() returns four uint32 values; we want a Pixel
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)}
}
So that I can compare the binary against what I expect I converted the rgba to 1 and 0s where 0 == black... it still doesn't match up not even close. Example
Help please. I'm out of ideas. PS. This site http://www.dcode.fr/binary-image, also opens the image and generates the data I'm expecting.
UPDATE:
This is the image i'm working with..
For example,
package main
import (
"bytes"
"fmt"
"image"
"os"
_ "image/jpeg"
)
func main() {
fName := "ggk3Z.jpg"
f, err := os.Open(fName)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// http://www.dcode.fr/binary-image
var txt bytes.Buffer
bounds := img.Bounds()
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
r, g, b, _ := img.At(x, y).RGBA()
bin := "0"
if float64((r+g+b))/3 > 0.5 {
bin = "1"
}
txt.WriteString(bin)
}
txt.WriteString("\n")
}
fmt.Fprint(os.Stdout, txt.String())
}

How to compare images with go?

In go image package, i don't see any methods which can be used to compare two images? Is it possible to do image comparison in go similar to ImageMagick?
If you are trying to compare two images and just need to boil it down to a single number, the following will work. This is useful in (for example) genetic algorithms, where you want to compare a set of candidates and choose the one that differs the least from a reference image:
Visit every pixel, break it down into its parts: R, G, B, & A (in go: image.At(x,y).RGBA())
Subtract the RGBA vals from their corresponding pixel vals in the reference image.
Square the differences, add them up.
Take the square root of the total sum.
This number will give you a rough idea of how much the images differ.
If you know that the two images are both instances of image.RGBA (or you can convert them), then you can do something even faster: just grab the bytes directly from RGBA.Pix. That's what I do here, and it's roughly 10x faster than doing img.At(x,y).RGBA() for every pixel pair:
func FastCompare(img1, img2 *image.RGBA) (int64, error) {
if img1.Bounds() != img2.Bounds() {
return 0, fmt.Errorf("image bounds not equal: %+v, %+v", img1.Bounds(), img2.Bounds())
}
accumError := int64(0)
for i := 0; i < len(img1.Pix); i++ {
accumError += int64(sqDiffUInt8(img1.Pix[i], img2.Pix[i]))
}
return int64(math.Sqrt(float64(accumError))), nil
}
func sqDiffUInt8(x, y uint8) uint64 {
d := uint64(x) - uint64(y)
return d * d
}
Try https://github.com/vitali-fedulov/images3. I wrote
this package to be able to find near duplicates. There is a live web-demo with the same algorithm, so you can get an idea how well the package suites your needs.
Inspired by George's answer.
The function below is not so fast, but it allows you to visually assess the difference in images.
func ImgCompare(img1, img2 image.Image) (int64, image.Image, error) {
bounds1 := img1.Bounds()
bounds2 := img2.Bounds()
if bounds1 != bounds2 {
return math.MaxInt64, nil, fmt.Errorf("image bounds not equal: %+v, %+v", img1.Bounds(), img2.Bounds())
}
accumError := int64(0)
resultImg := image.NewRGBA(image.Rect(
bounds1.Min.X,
bounds1.Min.Y,
bounds1.Max.X,
bounds1.Max.Y,
))
draw.Draw(resultImg, resultImg.Bounds(), img1, image.Point{0, 0}, draw.Src)
for x := bounds1.Min.X; x < bounds1.Max.X; x++ {
for y := bounds1.Min.Y; y < bounds1.Max.Y; y++ {
r1, g1, b1, a1 := img1.At(x, y).RGBA()
r2, g2, b2, a2 := img2.At(x, y).RGBA()
diff := int64(sqDiffUInt32(r1, r2))
diff += int64(sqDiffUInt32(g1, g2))
diff += int64(sqDiffUInt32(b1, b2))
diff += int64(sqDiffUInt32(a1, a2))
if diff > 0 {
accumError += diff
resultImg.Set(
bounds1.Min.X+x,
bounds1.Min.Y+y,
color.RGBA{R: 255, A: 255})
}
}
}
return int64(math.Sqrt(float64(accumError))), resultImg, nil
}
func sqDiffUInt32(x, y uint32) uint64 {
d := uint64(x) - uint64(y)
return d * d
}
With two of the current answers here, the images need to be the same size, or the comparison fails. A third answer here uses vitali-fedulov/images, which doesn't have any method to get the
difference between two images, only a Similar function that returns a bool determining if two images are similar. Further, the answer at Rosetta Code also fails if the images are different sizes.
So if I was to implement my own solution, first I would need to scale down the larger image. I found x/image/draw and nfnt/resize for that purpose, but I thought maybe I could find something, to kill two birds with one stone. To that end, I did find some packages that scale the images as needed, take a hash of each, and get the difference of the hashes. Here is corona10/goimagehash:
package main
import (
"github.com/corona10/goimagehash"
"image/jpeg"
"os"
)
func hash(name string) (*goimagehash.ImageHash, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
i, err := jpeg.Decode(f)
if err != nil {
return nil, err
}
return goimagehash.AverageHash(i)
}
Example:
package main
func main() {
a, err := hash("mb.jpg")
if err != nil {
panic(err)
}
b, err := hash("hqdefault.jpg")
if err != nil {
panic(err)
}
d, err := a.Distance(b)
if err != nil {
panic(err)
}
println(d)
}

Resources