Golang Iris Web - Serve 1x1 pixel - image

I'm new to Golang and trying to use a framework called iris.
My problem is how to serve 1x1 gif pixel, not using c.HTML context, but the way the browser title becomes 1x1 image gif. The image will be used for tracking.
Any help will be deeply appreciated.

Attention: I'm not really familiar with iris so the solution maybe not idiomatic.
package main
import (
"github.com/kataras/iris"
"image"
"image/color"
"image/gif"
)
func main() {
iris.Get("/", func(ctx *iris.Context) {
img := image.NewRGBA(image.Rect(0, 0, 1, 1)) //We create a new image of size 1x1 pixels
img.Set(0, 0, color.RGBA{255, 0, 0, 255}) //set the first and only pixel to some color, in this case red
err := gif.Encode(ctx.Response.BodyWriter(), img, nil) //encode the rgba image to gif, using gif.encode and write it to the response
if err != nil {
panic(err) //if we encounter some problems panic
}
ctx.SetContentType("image/gif") //set the content type so the browser can identify that our response is actually an gif image
})
iris.Listen(":8080")
}
Links for better understanding:
https://golang.org/pkg/image/
https://golang.org/pkg/image/gif
https://golang.org/pkg/image/color
https://golang.org/pkg/image/
https://godoc.org/github.com/valyala/fasthttp#Response

Related

efficient canvas refresh with fyne golang

I have the code below, It shows a window, generate an image as raster and then update the window content with it.
However, the setContent method is slow (with it I have 100% of 1 cpu core and almost 0 without).
I wonder if there is anything to do to do what I do here efficiently (modifiying underlaying raster, use gpu in anyway...) . I would like to be able to generate an image with raster and then display it at ~60 fps efficiently.
Any suggestion or other tools to do it better would be appreciated.
package main
import (
"image/color"
"math/rand"
"time"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
)
func main() {
myApp := app.New()
w := myApp.NewWindow("Raster")
go func() {
for {
time.Sleep(time.Millisecond * 500)
raster := canvas.NewRasterWithPixels(
func(_, _, w, h int) color.Color {
return color.RGBA{uint8(rand.Intn(255)),
uint8(rand.Intn(255)),
uint8(rand.Intn(255)), 0xff}
})
w.SetContent(raster)
}
}()
w.SetFullScreen(true)
// w.Resize(fyne.NewSize(120, 100))
w.ShowAndRun()
}
Setting the window content is a very expensive call as it has to re-layout all the content and check sizes etc.
Just call raster.Refresh() instead.
You could also cache the raster pixels in your widget so you don’t have to create a new image for every frame to refresh.

PNG Encode producing corrupt image

I'm reading a framebuffer from a video game console with golang - the buffer is in the format BRGA (which I then convert to RGBA). When I pass the information into the Go PNG Encoder, the image that comes out is not valid. The code i'm using is - where:
where data is a slice of RGBA pixels - 0x398000 in length, pitch is 5120, width is 1270, and height is 720)
img := &image.RGBA{
Pix: data,
Stride: pitch,
Rect: image.Rect(0, 0, width, height),
}
os.Remove("./img.png")
file, _ := os.Create("./img.png")
defer file.Close()
filewriter := bufio.NewWriter(file)
if err := png.Encode(filewriter, img); err != nil {
panic(err)
}
The expected outcome would be:
But the actual outcome is (only renders on Windows or when view in Chrome.. weird):
I have uploaded a binary dump of the RGBA slice if anybody would like it - https://1drv.ms/u/s!Ak-aZ3z7Ea8KwvUsqdP5OgWpZqxsGA
You are not flushing the buffered writer. You should do:
filewriter := bufio.NewWriter(file)
defer filewriter.Flush()
After this fix, I get a valid image:
Not a fix, and I want to comment but can't yet due to reputation, but will add to the Mac OS discrepancy.
The MacOS part of the problem appears to be new, showing up since either the latest 10.12.3 update, or something with Safari. I haven't narrowed down the source yet. But yes, there is something new about how a Mac system will encode/decode an image, causing it to be transparent or grey as a result. A project I am on is also suffering from this problem for the past few weeks and I'm still investigating where it breaks down.

Best way to remove borders / crop image

I am using a Go package (Go binding to ImageMagick's MagickWand C API) to ImageMagick where I'm removing borders from images (cropping). The way I am using the trim function can be found below.
Now the problem is the fuzzy factor. For example, if I set the value to 2000, the image (here is the source) still has some white images like these:
fuzz factor value 2000 --> result
fuzz factor value 10000 --> result
I have created a small html which illustrates the problem best. It contains both images: https://dl.dropboxusercontent.com/u/15684927/image-trim-problem.html
As you can see the source has some pixels on the bottom right corner which are causing the trouble. If I set the factor to 10000, I'm afraid that I will loose pixels on other pictures. If I set it on 2000, the trimming isn't done right in pictures like these.
So my actual question is: what is the best way to "crop" / "trim" images?
package main
import "gopkg.in/gographics/imagick.v1/imagick"
func main() {
imagick.Initialize()
defer imagick.Terminate()
inputFile := "tshirt-original.jpg"
outputFile := "trimmed.jpg"
mw := imagick.NewMagickWand()
// Schedule cleanup
defer mw.Destroy()
// read image
err := mw.ReadImage(inputFile)
if err != nil {
panic(err)
}
// first trim original image
// fuzz: by default target must match a particular pixel color exactly.
// However, in many cases two colors may differ by a small amount. The fuzz
// member of image defines how much tolerance is acceptable to consider two
// colors as the same. For example, set fuzz to 10 and the color red at
// intensities of 100 and 102 respectively are now interpreted as the same
// color for the purposes of the floodfill.
mw.TrimImage(10000)
// Set the compression quality to 95 (high quality = low compression)
err = mw.SetImageCompressionQuality(95)
if err != nil {
panic(err)
}
// save
err = mw.WriteImage(outputFile)
if err != nil {
panic(err)
}
}
Basically, your problem is that you have a high-frequency, high-amplitude artifact at the edge of your image. Or, put differently, a sharp, high peak at the edge, which, if you want to use trim, forces you to use such a high a fuzz-value to overcome this, that the algorithm also considers the 'actual content' as equal to the 'background' (border).
One solution here is to use a multi-step approach, whereby you first smooth out the edge artifacts and then apply trim to the resulting image. By smoothing it out, you get rid of the high peak and smear it out into a nice rolling hill. Rolling hills, in turn, can be easily trimmed with low fuzz values. This then provides you with the desired geometry which you can use to crop the original.
Specifically, let's take the original image:
Now, let's smooth out that ridge on the edge using a blur with a radius of 10 and a sigma of 10 through convert original.jpg -blur 10x10 10x10.jpg, which yields:
Now, you might notice that the artifacts on the edge have now pretty much disappeared.
We can now do a 'virtual' trim and ask ImageMagick what the result of the trim would be through convert 10x10.jpg -fuzz 2000 -format %# info:, which, according to the documentation gives you the "trim bounding box (without actually trimming)": 1326x1560+357+578%
Taking these values (except for the percentage sign) and using them for crop geometry, gives you the convert with crop command convert original.jpg -crop 1326x1560+357+578 cropped.jpg, which gives you:
Edit:
Now, since you want this as code, using imagick, here's the solution in code. It assumes you have the file stored as './data/original.jpg' and will store it as './data/trimmed.jpg'
package main
import (
"fmt"
"gopkg.in/gographics/imagick.v2/imagick"
)
func init() {
imagick.Initialize()
}
const originalImageFilename = "data/original.jpg"
func main() {
mw := imagick.NewMagickWand()
err := mw.ReadImage(originalImageFilename)
if err != nil {
fmt.Sprint(err.Error())
return
}
// Use a clone to determine what will happen
mw2 := mw.Clone()
mw2.BlurImage(10, 10)
mw2.TrimImage(2000)
_, _, xOffset, yOffset, err := mw2.GetImagePage()
if err != nil {
fmt.Sprint(err.Error())
return
}
trimmedWidth := mw2.GetImageWidth()
trimmedHeight := mw2.GetImageHeight()
mw2.Destroy()
mw.CropImage(trimmedWidth, trimmedHeight, xOffset, yOffset)
mw.WriteImage("data/trimmed.jpg")
mw.Destroy()
}

How to convert image.RGBA (image.Image) to image.Paletted?

I'm trying to create an animated GIF from a series of arbitrary non-paletted images. In order to create a paletted image, I need to come up with a palette somehow.
// RGBA, etc. images from somewhere else
var frames []image.Image
outGif := &gif.GIF{}
for _, simage := range frames {
// TODO: Convert image to paletted image
// bounds := simage.Bounds()
// palettedImage := image.NewPaletted(bounds, ...)
// Add new frame to animated GIF
outGif.Image = append(outGif.Image, palettedImage)
outGif.Delay = append(outGif.Delay, 0)
}
gif.EncodeAll(w, outGif)
Is there an easy way in golang stdlib to accomplish this?
It seems an automatic way of intelligently generating palettes is missing from the golang stdlib (correct me if I'm wrong here). But there seems to be a stub for providing your own Quantizer, which led me to the gogif project. (Which was the apparent source of image.Gif.)
I was able to borrow the MedianCutQuantizer from that project, defined here:
https://github.com/andybons/gogif/blob/master/mediancut.go
Which results in the following:
var subimages []image.Image // RGBA, etc. images from somewhere else
outGif := &gif.GIF{}
for _, simage := range subimages {
bounds := simage.Bounds()
palettedImage := image.NewPaletted(bounds, nil)
quantizer := gogif.MedianCutQuantizer{NumColor: 64}
quantizer.Quantize(palettedImage, bounds, simage, image.ZP)
// Add new frame to animated GIF
outGif.Image = append(outGif.Image, palettedImage)
outGif.Delay = append(outGif.Delay, 0)
}
gif.EncodeAll(w, outGif)
Instead of generating your own palette, you can also use on of the predefined (https://golang.org/pkg/image/color/palette/)
...
palettedImage := image.NewPaletted(bounds, palette.Plan9)
draw.Draw(palettedImage, palettedImage.Rect, simage, bounds.Min, draw.Over)
...

Using the Set() method of an image.Image or *image.RGBA

I am doing some practice with the Go image package with my free time this summer.
package main
import (
"os"
"image"
"image/png"
"image/color"
"log"
"fmt"
"reflect"
)
func main(){
file , err := os.OpenFile("C:/Sources/go3x3.png", os.O_RDWR, os.FileMode(0777))
if err != nil {
log.Fatal(err)
}
img , err := png.Decode(file)
if err != nil {
log.Fatal(err)
}
img.At(0,0).RGBA()
fmt.Println("type:", reflect.TypeOf(img))
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
fmt.Println("type:", reflect.TypeOf(m))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})
img.Set(0, 0, color.RGBA{136, 0, 21, 255})
}
The problem here is when I run it with the img.Set commented out I get this result
type: *image.RGBA
type: *image.RGBA
but when it's uncommented I get an error saying
img.Set undefined (type image.Image has no field or method Set)
I'm assuming I'm using reflect wrong, I'm still fully grasping the whole interface and type definitions in Go.
To expand on
a previous answer
answer:
png.Decode may create one of several different underlying image types (*image.Gray, *image.RGBA, *image.Paletted, *image.NRGBA, etc).
It returns whatever image it created as an image.Image interface which provides read only access to the data.
However, all (most?) of the actual image types it returns do implement the Set method for simple write access.
The way you can safely test for and use this method is via the existing draw.Image interface from the image/draw package. It's just this:
// From image/draw:
// Image is an image.Image with a Set method to change a single pixel.
type Image interface {
image.Image
Set(x, y int, c color.Color)
}
So you could do something like:
func drawablePNGImage(r io.Reader) (draw.Image, error) {
img, err := png.Decode(r)
if err != nil {
return nil, err
}
dimg, ok := img.(draw.Image)
if !ok {
return nil, fmt.Errorf("%T is not a drawable image type", img)
}
return dimg, nil
}
Playground (shows an example calling all the image.Image methods as well as Set).
Edit for Go1.17+:
Note that Go1.17 added draw.RGBA64Image with a SetRGBA64 method. As with draw.Image, all of the standard image types implement this. The advantage of this method is that the color values are not boxed in the color.Color interface type so doing many pixel operations can be faster.
Also note that Go1.18 added optimisations to the draw.Draw and draw.DrawMask fallback implementations for images that implement the optional draw.RGBA64Image and image.RGBA64Image interfaces.
reflect.TypeOf(img) gives you the reflection type of the value in the interface img, if its an interface. In this case, img is an interface, an image.Image which contains an *image.RGBA.
You can fix your code by converting img to an *image.RGBA, or more robustly, define an interface type with the right Set method, and convert img to that (the draw.Image interface in "image/draw" works perfectly for this, as noted by #DaveC). The interface type is preferable if you aren't sure that png.Decode will always give you an *image.RGBA for the .png files you have.
img.(*image.RGBA).Set(0, 0, color.RGBA{136, 0, 21, 255})
or
type Setter interface {
Set(x, y int, c color.Color)
}
img.(Setter).Set(0, 0, color.RGBA{136, 0, 21, 255})
or (probably best):
import "image/draw"
...
img.(draw.Image).Set(0, 0, color.RGBA{136, 0, 21, 255})
The compiler is correct, the image.Image type is an interface that does not include the Set() function.
I am not an expert at the image library but my cursory look at the types seems to suggest you can take an Image type and use the Bounds() method to get a image.Rectangle to create a new RGBA type as done previously in your example code.
// Your current image manipulation
m := image.NewRGBA(image.Rect(0, 0, 640, 480))
fmt.Println("type:", reflect.TypeOf(m))
m.Set(5, 5, color.RGBA{255, 0, 0, 255})
// You can create a image.RGBA type by passing the image.Rectangle
// returned from image.Image.Bounds()
m = image.NewRGBA(img.Bounds())
m.Set(0, 0, color.RGBA{136, 0, 21, 255})
This is a strict answer to your type issue but I don't have any gurantee it accomplishes your end goals.

Resources