Problems calculating stride and offset in WritePixels - c#

I am writing a Kinect application, where I use the color image from the sensor. I get a 640 x 480 color image, I copy the data from the sensor to a WriteableBitmap, with the WritePixels method. When I use the whole color image I have no issues. But I would like to use only the middle part of the image. But I can't get stride and or offset right?
To copy the whole image I do the following:
_colorImageWritableBitmap.WritePixels(
new Int32Rect(0, 0, colorImageFrame.Width, colorImageFrame.Height),
_colorImageData,
colorImageFrame.Width * Bgr32BytesPerPixel,
0);
As I mention I only want the middle part of the image. I would like to start at a width at 185px and take the next 270px, and stop there. And I use the the whole height.
My PixelFormat is bgr32, so to calculate the byte pr. pixel I use:
var bytesPrPixel = (PixelFormats.Bgr32.BitsPerPixel + 7)/8;
And my stride:
var stride = bytesPrPixel*width;
The writepixel method:
_colorImageWritableBitmap.WritePixels(
new Int32Rect(0, 0, colorImageFrame.Width, colorImageFrame.Height),
_colorImageData, stride, offset);
But when I change the width to other than 640, the image gets wrong (hidden in noise).
Can someone help me, to understand what I am doing wrong here?

You have to properly copy the pixels from the source bitmap. Assuming that the source colorImageFrame is also a BitmapSource, you would do it this way:
var width = 270;
var height = 480;
var x = (colorImageFrame.PixelWidth - width) / 2;
var y = 0;
var stride = (width * colorImageFrame.Format.BitsPerPixel + 7) / 8;
var pixels = new byte[height * stride];
colorImageFrame.CopyPixels(new Int32Rect(x, y, width, height), pixels, stride, 0);
Now you could write the pixel buffer to your WriteableBitmap by:
colorImageWritableBitmap.WritePixels(
new Int32Rect(0, 0, width, height), pixels, stride, 0);
Or instead of using WriteableBitmap, you just create a new BitmapSource, like:
var targetBitmap = BitmapSource.Create(
width, height, 96, 96, colorImageFrame.Format, null, pixels, stride);
However, the easiest way to create a crop of the source bitmap might be to used a CroppedBitmap like this:
var targetBitmap = new CroppedBitmap(
colorImageFrame, new Int32Rect(x, y, width, height));

Related

c# AR report with Photo gets distored when saved into PDF

AR report with Photo but gets distorted when saved into PDF.
I have a report that displays all student's photo. everything is fine when previewed as a report in Active Reports until it is saved. The saved file will become a pdf file. The pdf displays photo as distorted and non-uniformed sizes. I can say that it is not getting original file size or dimension (width & height) because some are displayed in desired size when the original dimension is bigger or even smaller, so I am not sure why it is selective. Some are displayed in desired size some are not.
I tried scaling the image before putting it in table, which displays it good and uniformed. But how come when saved into pdf it gets all distorted? Any idea how I can suppress it from scaling? thanks
public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(newImage))
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
I used it inside a function liek code below
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(image))
{
image = ScaleImage(image, 100, 100);
g.Clear(System.Drawing.Color.White);
System.Drawing.Pen pen = new System.Drawing.Pen(System.Drawing.Color.Black, 3);
pen.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
g.DrawRectangle(pen, 0, 0, image.Width - 5, image.Height - 5);
g.DrawString(" No\r\nPhoto", new System.Drawing.Font("Arial", 12), System.Drawing.Brushes.Black, new System.Drawing.PointF(20, 30));
}
I was able to figure it out. I just put a container first for every column before the image. So the image will be on top of the container. Set the property sizing of the image as FitProportional. Note that setting the sizing to "Fitproportional" will already make the dimension uniform but it will be skewed as it will follow the column size which is most likely a rectangle, so a container is necessary.

Value out of range with BitmapSource.Create

Working in WPF and trying to create a BitmapSource from scratch. The following code gives an Argument Exception " Value does not fall within the expected range." I can't figure out why. I'm still new to WPF and maybe I'm not using the pixel format correctly?
int width = 12;
int height = 14;
byte[] colorData = new byte[width * height * 4];
for(int i = 0; i < colorData.Length; i++)
{
colorData[i] = 155; //create all pixels same shade of gray
}
BitmapSource bitmap = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, colorData, width);
The last argument of the BitmapSource.Create method is the stride, i.e. the number of bytes per "scan line".
In case of a pixel format with four bytes per pixel, it is 4 * width:
var stride = 4 * width;
var bitmap = BitmapSource.Create(
width, height, 96, 96, PixelFormats.Bgra32, null, colorData, stride);

Blue and red channels swapped during conversion from bitmap to bitmapsource

I need to copy a bitmap that I receive from a camera into a BitmapSource in order to show it in a WPF application. Image arrives in PixelFormat.Format24bppRgb with a negative stride. I got this working by following code
//NOTE: image is in PixelFormat.Format24bppRgb
var bitmap = imageBuffer.Bitmap;
Image = new WriteableBitmap(bitmap.Width, bitmap.Height, 96, 96, PixelFormats.Rgb24, null);
var bitmapData = bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
var rowSize = bitmapData.Stride < 0 ? -bitmapData.Stride : bitmapData.Stride;
var bitmapPtr = bitmapData.Scan0;
var bitmapLine = new Int32Rect(0, 0, bitmap.Width, 1);
for (int line = 0; line < bitmap.Height; line++)
{
Image.WritePixels(bitmapLine, bitmapPtr, rowSize, rowSize, 0, line);
bitmapPtr += bitmapData.Stride;
}
bitmap.UnlockBits(bitmapData);
The problem I am running into is that that blue and red channels seem to be swapped. I could resolve the issue by creating the BitmapSource as Bgr24 but since in application I need to also do some image processing prior to conversion I would prefer to have things in a correct format prior to that. Am I doing something wrong in the conversion or is this some GDI peculiarity?
Note that if I apply the camera bitmap directly to a WinForms picture box, the image is displayed correctly. Also WriteableBitmap is only recreated for the sake of code brevity.
If the image format you receive is RGB but the stride is negative, then the image format is BGR as it's being read backwards.
Negative stride means it's an image bottom-up instead of top-down, usually the draw operations of Graphics would handle these things for you, but the Image class WritePixels doesn't allow to specify a negative stride, so you must reverse the pixel format (BGR)
The correct conversion from System.Drawing.Imaging.PixelFormat.Format24bppRgb to System.Windows.Media.PixelFormat is PixelFormats.Bgr24.
Negative stride has nothing to do with with the pixel format, it only describes vertical rotation of the image - top down for positive and bottom up for negative. If the stride had any effect on the pixel format, then the code below would have stored the images with reversed stride with blue and red channel swapped. This is not the case and the only effect seen is that one image is vertically rotated.
var wholeImage = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
bitmapData = bitmap.LockBits(wholeImage, ImageLockMode.ReadOnly, bitmap.PixelFormat);
var reverseStride = -bitmapData.Stride;
var reversedStartPtr = bitmapData.Scan0 + bitmapData.Stride * (bitmapData.Height - 1);
var reverseStrideBitmap = new Bitmap(bitmapData.Width, bitmapData.Height,
reverseStride, bitmapData.PixelFormat, reversedStartPtr);
bitmap.Save("original.png");
reverseStrideBitmap.Save("reversedStride.png");

Converting bitmap to special gray scale byte array

I need to create an image in memory (can be huge image!) and to extract from it byte array in the size of width x height. Each byte must have value of 0-255 (256 gray scale values: 0 for white and 255 for black).
The part of creating the image is easy, here is a simple example of my code:
img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
drawing.Clear(Color.Black);// paint the background
drawing.DrawString(text, font, Brushes.White, 0, 0);
Problem is to convert it to "my" special gray scale byte array. When I'm using any pixel format other then Format8bppIndexed, the byte array I'm getting from the bitmap is not in the size I need (width*length) so I need a conversion that takes too much time. When I'm using Format8bppIndexed I'm getting the byte array very fast and in the right size, but each byte/pixel is 0-15.
Changing the bitmap palette has no affect:
var pal = img.Palette;
for (int i = 1; i < 256; i++){
pal.Entries[i] = Color.FromArgb(255, 255, 255);
}
img.Palette = pal;
Any idea how to do it?
Edit: Full code:
// assume font can be Times New Roman, size 7500!
static private Bitmap DrawText(String text, Font font)
{
//first, create a dummy bitmap just to get a graphics object
var img = new Bitmap(1, 1);
var drawing = Graphics.FromImage(img);
//measure the string to see how big the image needs to be
var textSize = drawing.MeasureString(text, font);
//free up the dummy image and old graphics object
img.Dispose();
drawing.Dispose();
//create a new image of the right size (must be multiple of 4)
int width = (int) (textSize.Width/4) * 4;
int height = (int)(textSize.Height / 4) * 4;
img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
// paint the background
drawing.Clear(Color.Black);
drawing.DrawString(text, font, Brushes.White, 0, 0);
var bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
var newBitmap = new Bitmap(width, height, bmpData.Stride, PixelFormat.Format8bppIndexed, bmpData.Scan0);
drawing.Dispose();
return newBitmap;
}
private static byte[] GetGrayscleBytesFastest(Bitmap bitmap)
{
BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
You probably want to do this in two steps. First, create a 16bpp grayscale copy of your original image as described in Convert an image to grayscale.
Then, create your 8bpp image with the appropriate color table and draw the 16bpp grayscale image onto that image. That will do the conversion for you, converting the 16-bit grayscale values to your 256 different colors.
You should then have an 8bpp image with your 256 different shades of gray. You can then call LockBits to get access to the bitmap bits, which will be index values in the range 0 to 255.
I have solved this problem with ImageSharp
I calculate the gray value from the rgb values and then add it to the array.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
private static byte[] GetImageData(byte[] imageData)
{
using (var image = Image.Load<Rgba32>(imageData))
{
var buffer = new byte[image.Width * image.Height];
var index = 0;
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
Span<Rgba32> pixelRow = accessor.GetRowSpan(y);
for (int x = 0; x < pixelRow.Length; x++)
{
ref Rgba32 pixel = ref pixelRow[x];
buffer[index] = (byte)((pixel.R + pixel.G + pixel.B) / 3);
index++;
}
}
});
return buffer;
}
}

Create an image in WPF whose pixels are randomly chosen

I want to create an 800x600 image whose each pixel is randomly chosen to be either green or red. How can I do this in WPF?
See WriteableBitmap
const uint red = 0xFFFF0000,green = 0xFF00FF00;
var rnd = new Random();
var bmp = new WriteableBitmap(800, 600, 96, 96, PixelFormats.Pbgra32, null);
var data = Enumerable.Range(0, 800 * 600).Select(x => rnd.NextDouble() > 0.5 ? red : green).ToArray();
bmp.WritePixels(new Int32Rect(0, 0, 800, 600), data, bmp.BackBufferStride, 0);
That is a simple example and does not cover dealing with the bitmap stride, different pixel formats or alpha.

Categories

Resources