Scaling an Image Byte Array via Nearest Neighbor - c#

I am trying to scale an image by using a byte array. My plan was to use the nearest neighbor to find the pixel data for the new byte array but I am having some issues transforming the source data of my image. srcImage is of type Image and I am able to successfully convert it to a byte array and convert it back into an image, I am having an issue with scaling that image using its byte array. I use a MemoryStream (in my byteArrayToImage method) to convert the trgData back to an Image but I receive an ArguementException telling me that the 'new MemoryStream(byteArray)' parameter is not valid.
Does anyone happen to know what might be the issue or how to correctly scale a byte array from an Image? For right now, we can assume that I am only scaling and saving png files but I would like to eventually expand this to other image formats.
Here is my code for the copying and scaling of the image data:
byte[] srcData = ImageToByteArraybyImageConverter(srcImage);
// data transformations here
//
float srcRatio = srcImage.Width / srcImage.Height;
int bitsPerPixel = ((int)srcImage.PixelFormat & 0xff00) >> 8;
int bytesPerPixel = (bitsPerPixel + 7) / 8;
int srcStride = 4 * ((srcImage.Width * bytesPerPixel + 3) / 4);
// find max scale value
int scale = 3; // NOTE: temporary
// create new byte array to store new image
int width = srcImage.Width * scale;
int height = srcImage.Height * scale;
int stride = width;
byte[] trgData = new byte[height * stride];
// update the progress bar
progressBar1.Value = 10;
int i = -1; // index for src data
// copy pixel data
for (int n = 0; n < trgData.Length; n++)
{
if (n % stride == 0)
{
i++;
}
trgData[n] = srcData[i];
}
progressBar1.Value = 60;
// convert the pixel data to image
Image newImage = byteArrayToImage(trgData);
progressBar1.Value = 70;
if (newImage != null)
{
// save the image to disk
newImage.Save(newFileName);
progressBar1.Value = 100;
}
Let me know if you have any more questions, and thank you!
Edited: Here are the methods that load and save the byte array
private byte[] ImageToByteArraybyImageConverter(System.Drawing.Image image)
{
ImageConverter imageConverter = new ImageConverter();
byte[] imageByte = (byte[])imageConverter.ConvertTo(image, typeof(byte[]));
return imageByte;
}
private Image byteArrayToImage(byte[] byteArrayIn)
{
Image returnImage = null;
using (MemoryStream ms = new MemoryStream(byteArrayIn))
{
returnImage = Image.FromStream(ms);
}
return returnImage;
}

You are doing a very strange thing here:
int i = -1; // index for src data
// copy pixel data
for (int n = 0; n < trgData.Length; n++)
{
if (n % stride == 0)
{
i++;
}
trgData[n] = srcData[i];
}
After every width iterations (because stride == width) you increment the original index. That is, the whole first line of the new image will be filled with first byte of the original image, the second line - with the second byte etc.
Try this instead:
int targetIdx = 0;
for (int i = 0; i < height; ++i)
{
int iUnscaled = i / scale;
for (int j = 0; j < width; ++j) {
int jUnscaled = j / scale;
trgData[targetIdx++] = srcData[iUnscaled * origWidth + jUnscaled];
}
}
Note that it assumes BPP=1, that is it copies evey byte scale times horizontally and vertically. It's not hard to modify it for BPP more than 1.
Here is a demo illustrating both algorithms (comment the first line to see how your algo behaves)

Related

Convert ARGB to PARGB

I've been looking for a fast alternative method of SetPixel() and I have found this link : C# - Faster Alternatives to SetPixel and GetPixel for Bitmaps for Windows Forms App
So my problem is that I've an image and I want to create a copy as a DirectBitmap object but first I need to convert ARGB to PARGB so I used this code:
public static Color PremultiplyAlpha(Color pixel)
{
return Color.FromArgb(
pixel.A,
PremultiplyAlpha_Component(pixel.R, pixel.A),
PremultiplyAlpha_Component(pixel.G, pixel.A),
PremultiplyAlpha_Component(pixel.B, pixel.A));
}
private static byte PremultiplyAlpha_Component(byte source, byte alpha)
{
return (byte)((float)source * (float)alpha / (float)byte.MaxValue + 0.5f);
}
and Here's my copy code:
DirectBitmap DBMP = new DirectBitmap(img.Width, img.Height);
MyImage myImg = new MyImage(img as Bitmap);
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
Color PARGB = NativeWin32.PremultiplyAlpha(Color.FromArgb(myImg.RGB[i, j].Alpha,
myImg.RGB[i, j].R, myImg.RGB[i, j].G, myImg.RGB[i, j].B));
byte[] bitMapData = new byte[4];
bitMapData[3] = (byte)PARGB.A;
bitMapData[2] = (byte)PARGB.R;
bitMapData[1] = (byte)PARGB.G;
bitMapData[0] = (byte)PARGB.B;
DBMP.Bits[(i * img.Height) + j] = BitConverter.ToInt32(bitMapData, 0);
}
}
MyImage : a class containing a Bitmap object along with an array of struct RGB storing the colors of each pixel
However, this code gives me a messed up image. what am I doing wrong?
Bitmap data is organized horizontal line after horizontal line. Therefore, your last line should be:
DBMP.Bits[j * img.Width + i] = BitConverter.ToInt32(bitMapData, 0);

What is the WPF equivalent of displaying an Image on a Canvas using ImageData in Java SWT

What is the WPF equivalent of the following Java SWT code? I want to create an Image from a list of RGBA values and display on a Canvas.
private Image GetImage()
{
ImageData imageData = new ImageData(imageWidth, imageHeight,32,palette);
int pixelVecLoc=0;
for (int h = 0; h<imageHeight && (pixelVecLoc < currentImagePixelVec.size()); h++)
{
for (int w = 0; w<imageWidth && (pixelVecLoc < currentImagePixelVec.size()); w++)
{
int p = 0;
Pixel pixel = currentImagePixelVec.get(pixelVecLoc);
p = (pixel.Alpha<<24) | (pixel.Red<<16) | (pixel.Green<<8) | pixel.Blue;
imageData.setPixel(w, h, p);
pixelVecLoc++;
}
}
imageData = imageData.scaledTo(imageScaleWidth, imageScaleHeight);
Image image = ImageDescriptor.createFromImageData(imageData).createImage();
return image;
}
Then draw it on a Canvas:
gc.drawImage(image, 0, 0);
This is a short snippet showing how you can create a custom RGBA buffer and write pixel data to it (based on this example):
int width = 512;
int height = 256;
int stride = width * 4 + (width % 4);
int pixelWidth = 4; // RGBA (BGRA)
byte[] imageData = new byte[width * stride]; // raw byte buffer
for (int y = 0; y < height; y++)
{
int yPos = y * stride;
for (int x = 0; x < width; x++)
{
int xPos = yPos + x * pixelWidth;
imageData[xPos + 2] = (byte) (RedValue); // replace *Value with source data
imageData[xPos + 1] = (byte) (GreenValue);
imageData[xPos ] = (byte) (BlueValue);
imageData[xPos + 3] = (byte) (AlphaValue);
}
}
Then use the BitmapSource.Create Method (Int32, Int32, Double, Double, PixelFormat, BitmapPalette, IntPtr, Int32, Int32) method together with a PixelFormats:
BitmapSource bmp =
BitmapSource.Create(
width,
height,
96, // Horizontal DPI
96, // Vertical DPI
PixelFormats.Bgra32, // 32-bit BGRA
null, // no palette
imageData, // byte buffer
imageData.Length, // buffer size
stride); // stride
Note that the byte-order is reverse except the alpha component (BGRA) as shown in the snippet.
To transfer the result to canvas you can first create an Image, set the BitmapSource as Source and finally add that to the canvas:
// create image and set image as source
Image BmpImg = New Image();
BmpImg.Width = width;
BmpImg.Height = height;
BmpImg.Source = bmp;
// add image to canvas
canvas.Children.Add(BmpImg);
Canvas.SetLeft(BmpImg, 0); // to set position (x,y)
Canvas.SetTop(BmpImg, 0);

Return raw image bits in a BMP file

I have C# dll that I am using in Matlab to return the raw 8-bit grey scale bits that are stored in a BMP file. (Yes, I know Matlab can read the BMP, but this is a test, for a real system where the data will come from a video frame grabber).
Right now, the code below is returning 1078 bits of header information, that needs to be stripped, and it is also flipping the axis of the image (it comes out in mirror image). What is the best way to fix this (least lines, performance is not not a concern).
image = Image.FromFile(this.imagePath);
ImageConverter converter = new ImageConverter();
data = (byte[])converter.ConvertTo(image, typeof(byte[]));
Frame f = new Frame(data);
public class Frame
{
public byte[,] frameData = new byte[1024, 1024];
public Frame(byte[] data)
{
Buffer.BlockCopy(data, 0, frameData, 0, 1024 * 1024 * sizeof(byte));
}
}
Do you mean something like this:
BitArray bits = new BitArray(returnBytes);
BitArray flippedBits = new BitArray(bits);
for (int i = 0; i < bits.Length; i += width) {
for (int j = 0, k = width - 1; j < width; ++j, --k) {
flippedBits[i + j] = bits[i + k];
}
}
If you need to mirror picture upside-down, use this code:
BitArray bits = new BitArray(returnBytes);
BitArray flippedBits = new BitArray(bits);
for (int i = 0, j = bits.Length - width; i < bits.Length; i += width, j -= width) {
for (int k = 0; k < width; ++k) {
flippedBits[i + k] = bits[j + k];
}
}
Source:
Algorithm to vertically flip a bitmap in a byte array

C# how to show image in picturebox

I'm trying to display dicom image using openDicom.net. What should i correct here?
openDicom.Image.PixelData obraz = new openDicom.Image.PixelData(file.DataSet);
// System.Drawing.Bitmap obrazek = (Bitmap)Bitmap.FromFile(element);
pictureBox1.Image = obraz;
pictureBox1.Show();
PixelData is not an image. PixelData is raw image information. In my experience, most DICOM files will be using jpeg2000 images. In order to convert them to something usable by a PictureBox, you'll need to convert it to an Image. For raw monochrome types, you can make it into a System.Drawing.Bitmap using the following conversion:
openDicom.Image.PixelData obraz = new openDicom.Image.PixelData(file.DataSet);
Bitmap img = new System.Drawing.Bitmap(obraz.Columns, obraz.Rows, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
int resampleval = (int)Math.Pow(2, (obraz.BitsAllocated - obraz.BitsStored));
int pxCount = 0;
int temp = 0;
try
{
unsafe
{
BitmapData bd = img.LockBits(new Rectangle(0, 0, obraz.Columns, obraz.Rows), ImageLockMode.WriteOnly, img.PixelFormat);
for (int r = 0; r < bd.Height; r++)
{
byte* row = (byte*)bd.Scan0 + (r * bd.Stride);
for (int c = 0; c < bd.Width; c++)
{
temp = PixelData16[pxCount] / resampleval;
while (temp > 255)
temp = temp / resampleval;
row[(c * 3)] = (byte)temp;
row[(c * 3) + 1] = (byte)temp;
row[(c * 3) + 2] = (byte)temp;
pxCount++;
}
}
img.UnlockBits(bd);
}
}
catch
{
img = new Bitmap(10, 10);
}
pictureBox1.Image = img;
pictureBox1.Show();
For other image types, you'll need to do a similar conversion with the appropriate values. This conversion is strictly for monochrome types, and only after they have been converted from jpeg2000 to jpeg. Performing this operation on a jpeg2000 image will give you exactly half of the image filled with static and the other half completely empty.

How can I speed up this histogram class?

This is supposed to calculate the histogram of an 8-bit grayscale image. With a 1024x770 test bitmap, CreateTime ends up at around 890ms. How can I make this go (way, way) faster?
EDIT: I should mention that this doesn't actually compute the histogram yet, it only gets the values out of the bitmap. So I really should have asked, what is the fastest way to retrieve all pixel values from an 8-bit grayscale image?
public class Histogram {
private static int[,] values;
public Histogram(Bitmap b) {
var sw = Stopwatch.StartNew();
values = new int[b.Width, b.Height];
for (int w = 0; w < b.Width; ++w) {
for (int h = 0; h < b.Height; ++h) {
values[w, h] = b.GetPixel(w, h).R;
}
}
sw.Stop();
CreateTime = (sw.ElapsedTicks /
(double)Stopwatch.Frequency) * 1000;
}
public double CreateTime { get; set; }
}
The basic histogram algorithm is something like:
int[] hist = new hist[256];
//at this point dont forget to initialize your vector with 0s.
for(int i = 0; i < height; ++i)
{
for(int j = 0 ; j < widthl ++j)
{
hist[ image[i,j] ]++;
}
}
The algorithm sums how many pixels with value 0 you have, how many with value=1 and so on.
The basic idea is to use the pixel value as the index to the position of the histogram where you will count.
I have one version of this algorithm written for C# using unmanaged code (which is fast) I dont know if is faster than your but feel free to take it and test, here is the code:
public void Histogram(double[] histogram, Rectangle roi)
{
BitmapData data = Util.SetImageToProcess(image, roi);
if (image.PixelFormat != PixelFormat.Format8bppIndexed)
return;
if (histogram.Length < Util.GrayLevels)
return;
histogram.Initialize();
int width = data.Width;
int height = data.Height;
int offset = data.Stride - width;
unsafe
{
byte* ptr = (byte*)data.Scan0;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x, ++ptr)
histogram[ptr[0]]++;
ptr += offset;
}
}
image.UnlockBits(data);
}
static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi)
{
if (image != null)
return image.LockBits(
roi,
ImageLockMode.ReadWrite,
image.PixelFormat);
return null;
}
I hope I could help you.
You'll want to use the Bitmap.LockBits method to access the pixel data. This is a good reference on the process. Essentially, you're going to need to use unsafe code to iterate over the bitmap data.
Here's a copy/pastable version of the function I've come up w/ based on on this thread.
The unsafe code expects the bitmap to be Format24bppRgb, and if it's not, it'll convert the bitmap to that format and operate on the cloned version.
Note that the call to image.Clone() will throw if you pass in a bitmap using an indexed pixel format, such as Format4bppIndexed.
Takes ~200ms to get a histogram from an image 9100x2048 on my dev machine.
private long[] GetHistogram(Bitmap image)
{
var histogram = new long[256];
bool imageWasCloned = false;
if (image.PixelFormat != PixelFormat.Format24bppRgb)
{
//the unsafe code expects Format24bppRgb, so convert the image...
image = image.Clone(new Rectangle(0, 0, image.Width, image.Height), PixelFormat.Format24bppRgb);
imageWasCloned = true;
}
BitmapData bmd = null;
try
{
bmd = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
const int pixelSize = 3; //pixels are 3 bytes each w/ Format24bppRgb
//For info on locking the bitmap bits and finding the
//pixels using unsafe code, see http://www.bobpowell.net/lockingbits.htm
int height = bmd.Height;
int width = bmd.Width;
int rowPadding = bmd.Stride - (width * pixelSize);
unsafe
{
byte* pixelPtr = (byte*)bmd.Scan0;//starts on the first row
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
histogram[(pixelPtr[0] + pixelPtr[1] + pixelPtr[2]) / 3]++;
pixelPtr += pixelSize;//advance to next pixel in the row
}
pixelPtr += rowPadding;//advance ptr to the next pixel row by skipping the padding # the end of each row.
}
}
}
finally
{
if (bmd != null)
image.UnlockBits(bmd);
if (imageWasCloned)
image.Dispose();
}
return histogram;
}

Categories

Resources