Some of my resulting images are slanted, some are not.
Expected Result: (529x22)
Actual Result: (529x22)
Don't mind the different image sizes, these are screenshots. They are both 529x22.
The code I am using, I just got this from an answer on a question here at SO.
// some other method
byte[] pixels = new byte[size - 16];
Array.Copy(this.data, offset, pixels, 0, pixels.Length);
this.ByteToImage(w, h, pixels);
// builds the pixels to a image
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// bytes => not using this because it gives error
// eg. pixel.Length = 16032, bytes = 16064
int bytes = bmpData.Stride * bmp.Height;
Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
I'm confused because some works ok, not slanted. But others are slanted. What did I miss?
Update
As stated in the comments and answers, the problem is how I'm calculating stride. I'm still confused on how to do it but I tried this:
public static void RemovePadding(this Bitmap bitmap)
{
int bytesPerPixel = Image.GetPixelFormatSize(bitmap.PixelFormat) / 8;
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
var pixels = new byte[bitmapData.Width * bitmapData.Height * bytesPerPixel];
for (int row = 0; row < bitmapData.Height; row++)
{
var dataBeginPointer = IntPtr.Add(bitmapData.Scan0, row * bitmapData.Stride);
Marshal.Copy(dataBeginPointer, pixels, row * bitmapData.Width * bytesPerPixel, bitmapData.Width * bytesPerPixel);
}
Marshal.Copy(pixels, 0, bitmapData.Scan0, pixels.Length);
bitmap.UnlockBits(bitmapData);
}
But the result is (more slanted):
This seems to work here:
private Bitmap ByteToImage(int w, int h, byte[] pixels)
{
var bmp = new Bitmap(w, h, PixelFormat.Format16bppRgb565);
byte bpp = 2;
var BoundsRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
bmp.PixelFormat);
// copy line by line:
for (int y = 0; y < h; y++ )
Marshal.Copy(pixels, y * w * bpp, bmpData.Scan0 + bmpData.Stride * y, w * bpp);
bmp.UnlockBits(bmpData);
return bmp;
}
I use a loop to place each row of data at the right spot. The data do not include the padding, but the target address must do so.
Therefore we need to multiply the data access by the actual width * bytePerPixel but the target adress by the Stride, i.e. the length of the scanline, padded to the next multiple of four bytes. For width=300 it is stride=300, for width=301 it is stride=304..
Moving all pixel data in one step can only work when there is no padding, i.e. when the width is a multiple of 4.
This expects the stride to correspond to the width, without padding. There can be padding. The padding would "eat" some of the next line, which will therefore appear to shift left.
Since the padding breaks up the lines, the only real way to deal with it (other than using the same padding everywhere) is copying line by line. You can calculate the starting address of a line with bmpData.Scan0 + y * bmpData.Stride. Copy starting there, for every y.
// bytes => not using this because it gives error
Yes, because your array does not have padding. So you were telling it to copy more data than the array held.
Related
We are using a camera that acquires up to 60 frames per second, providing Bitmaps for us to use in our codebase.
As our wpf-app requires, these bitmaps are scaled based on a scaling factor; That scaling-process is by far the most limiting factor when it comes to actually displaying 60 fps. I am aware of new Bitmap(Bitmap source, int width, int height) which is obviously the simplest way to resize a Bitmap;
Nevertheless, I am trying to implement a "manual" approach using BitmapData and pointers. I have come up with the following:
public static Bitmap /*myMoBetta*/ResizeBitmap(this Bitmap bmp, double scaleFactor)
{
int desiredWidth = (int)(bmp.Width * scaleFactor),
desiredHeight = (int)(bmp.Height * scaleFactor);
var scaled = new Bitmap(desiredWidth, desiredHeight, bmp.PixelFormat);
int formatSize = (int)Math.Ceiling(Image.GetPixelFormatSize(bmp.PixelFormat)/8.0);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
BitmapData scaledData = scaled.LockBits(new Rectangle(0, 0, scaled.Width, scaled.Height), ImageLockMode.WriteOnly, scaled.PixelFormat);
unsafe
{
var srcPtr = (byte*)bmpData.Scan0.ToPointer();
var destPtr = (byte*)scaledData.Scan0.ToPointer();
int scaledDataSize = scaledData.Stride * scaledData.Height;
int nextPixel = (int)(1 / scaleFactor)*formatSize;
Parallel.For(0, scaledDataSize - formatSize,
i =>
{
for (int j = 0; j < formatSize; j++)
{
destPtr[i + j] = srcPtr[i * nextPixel + j];
}
});
}
bmp.UnlockBits(bmpData);
bmp.Dispose();
scaled.UnlockBits(scaledData);
return scaled;
}
Given scalingFactor < 1.
Actually using this algorithm does not seem to work, though. How are the bits of each pixel arranged in memory, exactly? My guess was that calling Image.GetPixelFormatSize() and deviding its result by 8 returns the number of bytes per pixel; But continuing to copy only formatSize amout of bytes every 1 / scaleFactor * formatSize byte results in a corrupted image.
What am I missing?
After some more research I bumped into OpenCV that has it's own .NET implementation with Emgu.CV, containing relevant methods for faster resizing.
My ResizeBitmap()-function has shrinked significantly:
public static Bitmap ResizeBitmap(this Bitmap bmp, int width, int height)
{
var desiredSize = new Size(width, height);
var src = new Emgu.CV.Image<Rgb, byte>(bmp);
var dest = new Emgu.CV.Image<Rgb, byte>(desiredSize);
Emgu.CV.CvInvoke.Resize(src, dest, desiredSize);
bmp.Dispose();
src.Dispose();
return dest.ToBitmap();
}
I have not tested performance thouroughly, but while debugging, this implementation reduced executiontime from 22ms with new Bitmap(source, width, height) to about 7ms.
This question already has answers here:
Split PNG into RGB and Alpha Channels
(2 answers)
Closed 3 years ago.
I'm using this code to save a bitmap as binary data.
Bitmap bmp = new Bitmap(screenWidth, position);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
File.WriteAllBytes(filename, bmp);
g.Dispose();
As I only need the first channel's values, is it possible to retrieve that from the bitmap? Performance is essential.
You're almost there, but there are a few key details missing:
Instead of using bmp.PixelFormat, force the pixel format for the BitmapData object to PixelFormat.Format32BppArgb, then you're 100% sure what structure you will get, and in 32-bit mode, the stride will always exactly match a predictable width * 4. If you don't do this, you may get unexpected results if the read image happens to be paletted or some sort of 16bpp format where each pixel can't be divided into simple colour component bytes.
Loop over the data and extract the channel. The order of the letters 'ARGB' refers to the a hexadecimal value 0xAARRGGBB (like, for example, 0xFF428ED0), which is a little-endian Uint32 value, meaning the actual order of the colour component bytes is the reverse: { BB, GG, RR, AA }.
So, to extract your channel:
// Channels are: B=0, G=1, R=2, A=3
Int32 channel = 1 // for this example, extract the Green channel.
Int32 width;
Int32 height;
Byte[] rgbaValues;
using (Bitmap bmp = new Bitmap(screenWidth, position))
using (Graphics g = Graphics.FromImage(bmp))
{
width = bmp.Width
height = bmp.Height;
g.CopyFromScreen(screenLeft, screenTop, 0, 0, bmp.Size);
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Int32 bytes = bmpData.Stride * bmp.Height;
rgbaValues = new byte[bytes];
Marshal.Copy(bmpData.Scan0, rgbValues, 0, bytes);
bmp.UnlockBits(bmpData);
g.Dispose();
}
Byte[] channelValues = new byte[width * height];
Int32 lineStart = 0;
Int32 lineStartChannel = 0;
for (Int32 y = 0; y < height; ++y)
{
Int32 offset = lineStart;
Int32 offsetChannel = lineStartChannel;
for (Int32 x = 0; x < width; ++x)
{
// For reference:
//Byte blue = rgbaValues[offset + 0];
//Byte green = rgbaValues[offset + 1];
//Byte red = rgbaValues[offset + 2];
//Byte alpha = rgbaValues[offset + 3];
channelValues[offsetChannel] = rgbaValues[offset + channel];
offset += 4;
offsetChannel++;
}
lineStart += stride;
lineStartChannel += width;
}
File.WriteAllBytes(filename, channelValues);
This just saves the data as byte array. If you want to write it as image, the simplest way is probably to make an 8-bit bitmap, open a BitmapData object on it, and write the lines into it one by one, and then set its colour palette to a generated range from 0,0,0 to 255,255,255.
I posted a function that takes a byte array, image dimensions and a palette and makes an image out of it in this answer.
I have raw pixel data coming from a camera in RGB8 format which I need to convert to a Bitmap. However, the Bitmap PixelFormat only seems to support RGB 16, 24, 32, and 48 formats.
I attempted to use PixelFormat.Format8bppIndexed, but the image appears discolored and inverted.
public static Bitmap CopyDataToBitmap(byte[] data)
{
var bmp = new Bitmap(640, 480, PixelFormat.Format8bppIndexed);
var bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
Is there any other way to convert this data type correctly?
This creates a linear 8-bit grayscale palette in your image.
bmp.UnlockBits(bmpData);
var pal = bmp.Palette;
for (int i = 0; i < 256; i++) pal.Entries[i] = Color.FromArgb(i, i, i);
bmp.Palette = pal;
return bmp;
You will still need to invert the scan lines, maybe like this:
for (int y = 0; y < bmp.Height; y++)
Marshal.Copy(data, y * bmp.Width,
bmpData.Scan0 + ((bmp.Height - 1 - y) * bmpData.Stride), bmpData.Stride);
For a mobile application I have to generate a 1-bit-bitmap out of a 24-bit-bitmap. The problem is, that the result isn't correct, so i made this little project to try it on my desktop-pc. the creation works, but the result isn't ok, as you can see.
You can hardly read anything because a lot of bits aren't at the right position anymore but moved some pixels left or right.
This is the code i use for creation:
int z = 0;
int bitNumber = 0;
//the new 1Bit-byte-Array
byte[] oneBitImage = new byte[(bmp.Height * bmp.Width) / 8];
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* p = (byte*)(void*)bmData.Scan0.ToPointer();
int stopAddress = (int)p + bmp.Height * bmData.Stride;
while ((int)p != stopAddress)
{
if (*p < 128) // is black
oneBitImage[z] = (byte)(oneBitImage[z] | Exp(bitNumber)); //Set a Bit on the specified position
p += 3;
bitNumber++;
if (bitNumber == 8)
{
bitNumber = 0;
z++;
}
}
}
bmp.UnlockBits(bmData);
//Convert into 1-bit-bmp to check result
Bitmap newbmp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format1bppIndexed);
BitmapData bmpData = newbmp.LockBits(
new Rectangle(0, 0, newbmp.Width, newbmp.Height),
ImageLockMode.WriteOnly, newbmp.PixelFormat);
Marshal.Copy(oneBitImage, 0, bmpData.Scan0, oneBitImage.Length);
newbmp.UnlockBits(bmpData);
newbmp.Save(fileName, ImageFormat.Bmp);
Short explanation:
I run through every third byte, and if this byte - the first one of a 3-byte-group (pixel in 24-bit) - is lower than 128 I put a bit at the specified position.
EXP gives me the exponent...
Switch the bits in every output byte around. Bit 7 should be bit 0, etc.
I would convert three bytes to a "real" color (long value or similar) and see if the result is bigger than half of 16.7m .
I have a byte[] array received in TCP Client.The array contains a 24-bit RGB Bitmap file.How to create that bitmap file with given Width ,Height and data?
In C++ I use this
int WriteBitmapFile(const char *filename, int width, int height, unsigned char *imageData)
{
FILE *filePtr; // file pointer
BITMAPFILEHEADER bitmapFileHeader; // bitmap file header
BITMAPINFOHEADER bitmapInfoHeader; // bitmap info header
DWORD imageIdx; // used for swapping RGB->BGR
unsigned char tempRGB; // used for swapping
// open file for writing binary mode
filePtr = fopen(filename, "wb");
if (!filePtr)
return 0;
// define the bitmap file header
bitmapFileHeader.bfSize = sizeof(BITMAPFILEHEADER);
bitmapFileHeader.bfType = 0x4D42;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// define the bitmap information header
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 32; // 24-bit
bitmapInfoHeader.biCompression = BI_RGB; // no compression
bitmapInfoHeader.biSizeImage = width * abs(height) * 4; // width * height * (RGB bytes)
bitmapInfoHeader.biXPelsPerMeter = 0;
bitmapInfoHeader.biYPelsPerMeter = 0;
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
bitmapInfoHeader.biWidth = width; // bitmap width
bitmapInfoHeader.biHeight = height; // bitmap height
// switch the image data from RGB to BGR
for(imageIdx = 0; imageIdx < bitmapInfoHeader.biSizeImage; imageIdx+=4)
{
tempRGB = imageData[imageIdx];
imageData[imageIdx] = imageData[imageIdx + 2];
imageData[imageIdx + 2] = tempRGB;
}
// write the bitmap file header
fwrite(&bitmapFileHeader, 1, sizeof(BITMAPFILEHEADER), filePtr);
// write the bitmap info header
fwrite(&bitmapInfoHeader, 1, sizeof(BITMAPINFOHEADER), filePtr);
// write the image data
fwrite(imageData, 1, bitmapInfoHeader.biSizeImage, filePtr);
// close our file
fclose(filePtr);
// Success
return 1;
}
How could I do that in C#?
If the array actually contains a bitmap file, then you can just save the bytes as a file:
File.WriteAllBytes(fileName, imageData);
If the array contains only raw pixel data, you can create a Bitmap object using the data:
unsafe {
fixed (byte* ptr = imageData) {
using (Bitmap image = new Bitmap(width, height, stride, PixelFormat.Format24bppRgb, new IntPtr(ptr))) {
image.Save(fileName);
}
}
}
The stride value is the number of bytes between the scan lines. If there is no padding between the scan lines, it's width * 3 for a 24bpp format.
This method uses the data in the array without creating another copy of the entire image in memory (which is why it needs the stride value).
If the bitmap data is stored upside down in the array, the stride value should be negative, and the pointer should be the start of the last scan line in memory (ptr + stride * (height - 1)).
I can't test it using the stream you will be receiving, but this should work.
int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
using (var stream = new MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
Marshal.Copy(imageData, 0, bmpData.Scan0, imageData.Length);
bmp.UnlockBits(bmpData);
bmp.Save(filename);
}
return 1;
}
I'd recommend making a Bitmap in C#, and letting it save itself.
For an example, see this post. (Particularly, the last response is correct.)
this is one way of doing it, here i have created a custom Event args that contains the size at which the image was stored as a byte array. You may not need to bother with this, this was code i created to retreive images from a byte array that a gige camera was storing to so for me this made sence.
public Bitmap ShowImage(byte[] sender, EventImageParams e)
{
Bitmap bitmap = new Bitmap(e.width, e.height, PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(sender, 0, pNative, (e.width * e.height * 3));
//
bitmap.UnlockBits(bmData);
return bitmap;
}