I want to retrieve an image from a specific application and convert it to image.Image for later use.
What I have now is a HBITMAP from windows API call. After trying a lots, I can't manage to convert the created HBITMAP to an image.Image (or at least an []byte).
rc := w32.GetClientRect(hwnd)
if rc != nil {
// create
HDCScreen := w32.GetWindowDC(hwnd)
hdc := w32.CreateCompatibleDC(HDCScreen)
hbmp := w32.CreateCompatibleBitmap(HDCScreen, int(rc.Right)-int(rc.Left), int(rc.Bottom)-int(rc.Top))
w32.SelectObject(hdc, w32.HGDIOBJ(hbmp))
// Print to memory hdc
w32.PrintWindow(hwnd, hdc, 0x00000002)
// ------------------------------------------------
var bmpInfo *w32.BITMAPINFO = &w32.BITMAPINFO{}
bmpInfo.BmiHeader.BiSize = uint32(unsafe.Sizeof(bmpInfo.BmiHeader))
firstDIBits := w32.GetDIBits(HDCScreen, hbmp, 0, 0, nil, bmpInfo, w32.DIB_RGB_COLORS)
fmt.Printf("firstDIBits: %v\n", firstDIBits)
var lpPixels *[]byte
bmpInfo.BmiHeader.BiBitCount = 32
bmpInfo.BmiHeader.BiCompression = w32.BI_RGB
bmpInfo.BmiHeader.BiHeight = int32(math.Abs(float64(bmpInfo.BmiHeader.BiHeight)))
bmpInfo.BmiHeader.BiCompression = w32.BI_RGB
secondDIBits := w32.GetDIBits(hdc, hbmp, 0, uint(bmpInfo.BmiHeader.BiHeight), unsafe.Pointer(lpPixels), bmpInfo, w32.DIB_RGB_COLORS)
fmt.Printf("secondDIBits: %v\n", secondDIBits)
fmt.Printf("lpPixels: %v\n", lpPixels)
// ------------------------------------------------
// copy to clipBoard
w32.OpenClipboard(0)
w32.EmptyClipboard()
w32.SetClipboardData(w32.CF_BITMAP, w32.HANDLE(hbmp))
w32.CloseClipboard()
// release
w32.DeleteDC(hdc)
w32.DeleteObject(w32.HGDIOBJ(hbmp))
w32.ReleaseDC(0, HDCScreen)
}
Both of GetDIBits() call return 1 but lpPixels is always nil.
The Bitmap class inherits from the Image class.In my opinion, you couldn't need the conversion.
According to the Doc:GetDIBits function
[out] lpvBits
A pointer to a buffer to receive the bitmap data. If this parameter is
NULL, the function passes the dimensions and format of the bitmap to
the BITMAPINFO structure pointed to by the lpbi parameter.
I don't know much about Go language, but the lpvBits parameter of GetDIBits seem incorrect. I suggest you could refer to the threads:
Using GetDIBits to load a bitmap
How should I use the GetDIBits function
Finally made it. Posting the code in case it can help someone else. :)
rect := image.Rect(0, 0, 1920, 1080)
img := image.NewRGBA(rect)
rc := w32.GetClientRect(hwnd)
if rc != nil {
width := int(rc.Right) - int(rc.Left)
height := int(rc.Bottom) - int(rc.Top)
hwndwin := win.HWND(hwnd)
hdc := win.GetDC(hwndwin)
if hdc == 0 {
panic("GetDC failed")
}
defer win.ReleaseDC(hwndwin, hdc)
memory_device := win.CreateCompatibleDC(hdc)
if memory_device == 0 {
panic("CreateCompatibleDC failed")
}
defer win.DeleteDC(memory_device)
bitmap := win.CreateCompatibleBitmap(hdc, int32(width), int32(height))
if bitmap == 0 {
panic("CreateCompatibleBitmap failed")
}
defer win.DeleteObject(win.HGDIOBJ(bitmap))
var header win.BITMAPINFOHEADER
header.BiSize = uint32(unsafe.Sizeof(header))
header.BiPlanes = 1
header.BiBitCount = 32
header.BiWidth = int32(width)
header.BiHeight = int32(-height)
header.BiCompression = win.BI_RGB
header.BiSizeImage = 0
// GetDIBits balks at using Go memory on some systems. The MSDN example uses GlobalAlloc
// https://docs.microsoft.com/en-gb/windows/desktop/gdi/capturing-an-image
bitmapDataSize := uintptr(((int64(width)*int64(header.BiBitCount) + 31) / 32) * 4 * int64(height))
hmem := win.GlobalAlloc(win.GMEM_MOVEABLE, bitmapDataSize)
defer win.GlobalFree(hmem)
memptr := win.GlobalLock(hmem)
defer win.GlobalUnlock(hmem)
old := win.SelectObject(memory_device, win.HGDIOBJ(bitmap))
if old == 0 {
panic("SelectObject failed")
}
defer win.SelectObject(memory_device, old)
if !win.BitBlt(memory_device, 0, 0, int32(width), int32(height), hdc, int32(0), int32(0), win.SRCCOPY) {
panic("BitBlt failed")
}
if win.GetDIBits(hdc, bitmap, 0, uint32(height), (*uint8)(memptr), (*win.BITMAPINFO)(unsafe.Pointer(&header)), win.DIB_RGB_COLORS) == 0 {
panic("GetDIBits failed")
}
i := 0
src := uintptr(memptr)
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
v0 := *(*uint8)(unsafe.Pointer(src))
v1 := *(*uint8)(unsafe.Pointer(src + 1))
v2 := *(*uint8)(unsafe.Pointer(src + 2))
// BGRA => RGBA, and set A to 255
img.Pix[i], img.Pix[i+1], img.Pix[i+2], img.Pix[i+3] = v2, v1, v0, 255
i += 4
src += 4
}
}
Related
I have a C library and function that expects a pointer to byte array that contains a 24 bit bitmap in RGB format. Alpha channel is not important and can be truncated. I've tried something like this:
func load(filePath string) *image.RGBA {
imgFile, err := os.Open(filePath)
if err != nil {
fmt.Printf("Cannot read file %v\n", err)
}
defer imgFile.Close()
img, _, err := image.Decode(imgFile)
if err != nil {
fmt.Printf("Cannot decode file %v\n", err)
}
return img.(*image.RGBA)
}
img := load("myimg.png")
bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
// Convert to RGB? Probably not...
newImg := image.NewNRGBA(image.Rect(0, 0, width, height))
draw.Draw(newImg, newImg.Bounds(), img, bounds.Min, draw.Src)
// Pass image pointer to C function.
C.PaintOnImage(unsafe.Pointer(&newImg.Pix[0]), C.int(newImg.Bounds().Dy()), C.int(newImg.Bounds().Dx())
However, it seems that NRGBA is also built on 4 bytes per pixel. I could solve this probably by using GoCV but this seems like overkill for such simple task. Is there a way to do this in a simple and efficient manner in Go?
There is no RGB image type in the standard library, but you can assemble your RGB array pretty easily:
bounds := img.Bounds()
rgb := make([]byte, bounds.Dx()*bounds.Dy()*3)
idx := 0
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
offs := img.PixOffset(x, y)
copy(rgb[idx:], img.Pix[offs:offs+3])
idx += 3
}
}
The img.Pix data holds the 4-byte RGBA values. The code above just copies the leading 3-byte RGB values of all pixels.
Since lines are continuous in the Pix array, you can improve the above code by only calling PixOffset onces per line, and advance by 4 bytes for every pixel. Also manually copying 3 bytes may be faster than calling copy() (benchmark if it matters to you):
bounds := img.Bounds()
rgb := make([]byte, bounds.Dx()*bounds.Dy()*3)
idx := 0
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
offs := img.PixOffset(bounds.Min.X, y)
for x := bounds.Min.X; x < bounds.Max.X; x++ {
rgb[idx+0] = img.Pix[offs+0]
rgb[idx+1] = img.Pix[offs+1]
rgb[idx+2] = img.Pix[offs+2]
idx += 3
offs += 4
}
}
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)});
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")
}
when recording the microphone, the recorded chunks was raw PCM8 format and I was able to send it and play it by changing bitDepthInBytes = 2 without any noise, but when I've sent encoded opus frames through a network and decode them to PCM16, I couldn't play them unless I convert them to PCM8 but it was noisy. Here is my code:
const sampleRate = 48000
const channels = 1
....
....
dec, err := opus.NewDecoder(sampleRate, channels)
if err != nil {
fmt.Println(err)
return
}
var frameSizeMs float32 = 20
frameSize := int(channels * frameSizeMs * sampleRate / 1000)
pcm := make([]int16, frameSize)
// (sampleRate int, channelNum int, bitDepthInBytes int, bufferSizeInBytes int)
context, err := oto.NewContext(sampleRate, channels, 1, frameSize*2)
if err != nil {
log.Fatal(err)
}
player := context.NewPlayer()
...
...
_, err := dec.Decode(data, pcm)
if err != nil {
fmt.Println(err)
}
var mask uint16 = 0x8000
pcm8 := make([]byte, frameSize)
for i := 0; i < frameSize; i++ {
// using this work and play sound but it has noise
pcm8[i] = byte((uint16(pcm[i]) ^ mask) >> 8)
}
_, _ = player.Write(pcm8)
By reading this, I was able to know how to format your PCM buffer to playable audio bytes https://github.com/philfrei/AudioCue/blob/master/src/main/java/com/adonax/audiocue/AudioCue.java, this the snippet I've used:
public static byte[] fromBufferToAudioBytes(byte[] audioBytes, float[] buffer)
{
for (int i = 0, n = buffer.length; i < n; i++)
{
buffer[i] *= 32767;
audioBytes[i*2] = (byte) buffer[i];
audioBytes[i*2 + 1] = (byte)((int)buffer[i] >> 8 );
}
return audioBytes;
}
And this is what I've updated in my code
pcm8 := make([]byte, frameSize * 2)
for i := 0; i < frameSize; i++ {
//pcm[i] *= 32767 // uncomment when pcm array is float insteand of int16
pcm8[i*2] = byte(uint16(pcm[i]))
pcm8[i*2 + 1] = byte(uint16(pcm[i]) >> 8)
}
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!