How to implement Custom cropping using golang - image

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

Related

Gaussian Blur implementation generates weird output

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

Does Go have library which opens paletted png of 2 bit color-depth?

How do you read a palette-based PNG image with Go?
For an image in Python I can simply do:
from PIL import Image
im = Image.open('image.png')
pix = im.load()
for i in range(100):
for j in range(100):
print(pix[i, j])
Using Go:
f, err := os.Open("image.png")
if err != nil {
log.Fatal(err)
}
defer f.Close()
pal, ok := cfg.ColorModel.(color.Palette) // ok is true
if ok {
for i := range pal {
cr, cg, cb, ca := pal[i].RGBA()
fmt.Printf("pal[%3d] = %5d,%5d,%5d,%5d\n", i, cr, cg, cb, ca)
}
}
img, err := png.Decode(f)
if err != nil {
log.Fatal(err) // Fails here!!
}
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
log.Println("img.At(x, y):", img.At(x, y))
}
fmt.Print("\n")
}
It'll go to throws "png: invalid format: not a PNG file" when decoding.
If I use the file command on Mac shell it says:
image.png: PNG image data, 100 x 100, 2-bit colormap, non-interlaced
And VsCode render the image just fine.
I tried it both on an image created from Adobe Illustrator and one generated from the below code. Both run into the same error:
func createPNG() {
// Create a new image with the desired dimensions
img := image.NewPaletted(image.Rect(0, 0, 100, 100), color.Palette{
color.RGBA{255, 0, 0, 255}, // Red
color.RGBA{0, 255, 0, 255}, // Green
color.RGBA{0, 0, 255, 255}, // Blue
})
// Set the pixel colors in the image
for x := 0; x < 100; x++ {
for y := 0; y < 100; y++ {
switch {
case x < 50 && y < 50:
img.SetColorIndex(x, y, 0) // Set the pixel at (x, y) to red
case x >= 50 && y < 50:
img.SetColorIndex(x, y, 1) // Set the pixel at (x, y) to green
case x < 50 && y >= 50:
img.SetColorIndex(x, y, 2) // Set the pixel at (x, y) to blue
default:
img.SetColorIndex(x, y, 3) // Set the pixel at (x, y) to transparent
}
}
}
// Create a new file to save the PNG image
file, err := os.Create("image.png")
if err != nil {
panic(err)
}
defer file.Close()
// Encode the image as a PNG and save it to the file
err = png.Encode(file, img)
if err != nil {
panic(err)
}
}
The problem in your case seems to be not the format of the image, but rather the way you use the image file.
I assume you first pass it to image.DecodeConfig() (the code doesn't show it but cfg must have been initialized) and later to image.Decode().
The problem is that after the first call your file has an offset but the second call assumes that it is reading from the beginning of the file.
You can solve this by rewinding the file after reading the configuration:
file.Seek(0, io.SeekStart)

How do I draw a filled Rectangular box on jpeg if given two pixels values like for eg:- pt1(0,0) and pt2(480, 240)?

I want to open a jpeg file from localdisk and draw a filled Rectangular box on it in Go, if i give two pixel points value like pt1(0,0) and pt2(480, 240)
Have a look here. You need to decode the image from JPEG, set the pixels in your rectangle to black, then re-encode to JPEG.
Here is a code example, it works if the image is in the same folder as the executable and you give the path like myimg.jpg:
package main
import (
"fmt"
"image"
"image/color"
"image/jpeg"
"log"
"os"
)
func main() {
err := addBlackRectangle("myimg.jpg", 500, 500, 1000, 1000)
if err != nil {
log.Fatal(err)
}
}
func addBlackRectangle(imgPath string, x1, y1, x2, y2 int) (err error) {
r, err := os.Open(imgPath)
if err != nil {
return
}
img, err := jpeg.Decode(r)
if err != nil {
return
}
rect1 := img.Bounds()
rect2 := image.Rect(x1, y1, x2, y2)
if !rect2.In(rect1) {
err = fmt.Errorf("error: rectangle outside image")
return
}
rgba := image.NewRGBA(rect1)
for x := rect1.Min.X; x <= rect1.Max.X; x++ {
for y := rect1.Min.Y; y <= rect1.Max.Y; y++ {
p := image.Pt(x, y)
if p.In(rect2) {
rgba.Set(x, y, color.Black)
} else {
rgba.Set(x, y, img.At(x, y))
}
}
}
outputFile := "rect-" + imgPath
w, err := os.Create(outputFile)
defer w.Close()
err = jpeg.Encode(w, rgba, nil)
return
}

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

Processing: Image with rounded corners

I'm drawing a section of an image, however I'd like to apply rounded corners to it. I can't find any way of doing this.
In the draw() method:
img_section = img.get(gaze_x, gaze_y, gaze_size_x, gaze_size_y);
image(img_section, gaze_x, gaze_y);
You could copy the image and then manually set the corner pixels using the set() function.
You could just draw a rounded rectangle around the image- if the image will be placed on a background with a single color, just draw a rounded rectangle with the same color as the image.
Or you could come up with an image mask and draw that on top of your image.
package utils
import (
"ddkt365-poster/library/log"
"image"
"image/color"
"math"
)
// Settable Settable
type Settable interface {
Set(x, y int, c color.Color)
}
var empty = color.RGBA{255, 255, 255, 0}
// Convert Convert
func Convert(m *image.Image, rate float64) {
b := (*m).Bounds()
w, h := b.Dx(), b.Dy()
r := (float64(min(w, h)) / 2) * rate
log.Error("bounds:%v", r)
sm, ok := (*m).(Settable)
if !ok {
// Check if image is YCbCr format.
ym, ok := (*m).(*image.YCbCr)
if !ok {
log.Error("errInvalidFormat")
return
}
*m = yCbCrToRGBA(ym)
sm = (*m).(Settable)
}
// Parallelize?
for y := 0.0; y <= r; y++ {
l := math.Round(r - math.Sqrt(2*y*r-y*y))
for x := 0; x <= int(l); x++ {
sm.Set(x-1, int(y)-1, empty)
}
for x := 0; x <= int(l); x++ {
sm.Set(w-x, int(y)-1, empty)
}
for x := 0; x <= int(l); x++ {
sm.Set(x-1, h-int(y), empty)
}
for x := 0; x <= int(l); x++ {
sm.Set(w-x, h-int(y), empty)
}
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func yCbCrToRGBA(m image.Image) image.Image {
b := m.Bounds()
nm := image.NewRGBA(b)
for y := 0; y < b.Dy(); y++ {
for x := 0; x < b.Dx(); x++ {
nm.Set(x, y, m.At(x, y))
}
}
return nm
}
// Image with rounded corners (Go image/draw package)
if i.BorderRadius > 0 {
utils.Convert(&img, (float64(i.BorderRadius) / 100))
}
draw.Draw(canvs, img.Bounds().Add(image.Pt(i.X, i.Y)), img, image.ZP, draw.Over)

Resources