Copying a byte array to a bitmap using unsafe block - c#

I'm trying to squeeze the best performance out of some imaging code and I've hit a wall.
As far as my knowledge goes it should be possible to speed up the process using pointers but my experience with them is very limited and finding good documentation to read and understand is proving difficult.
Am I correct? Could someone show an annotated example of the code converted to help me understand the process.
public void UpdatePixelIndexes(IEnumerable<byte[]> lineIndexes)
{
int width = this.Image.Width;
int height = this.Image.Height;
IEnumerator<byte[]> indexesIterator = lineIndexes.GetEnumerator();
for (int rowIndex = 0; rowIndex < height; rowIndex++)
{
indexesIterator.MoveNext();
BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, rowIndex, width, rowIndex + 1), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
{
Marshal.Copy(indexesIterator.Current, 0, data.Scan0, width);
}
finally
{
this.Image.UnlockBits(data);
}
}
}

It's unlikely you actually need unsafe here. As suggested, you should just stop locking/unlocking the bitmap for every scan line. Instead, do this:
public void UpdatePixelIndexes(IEnumerable<byte[]> lineIndexes)
{
int width = this.Image.Width;
int height = this.Image.Height;
int rowIndex = 0;
BitmapData data = this.Image.LockBits(Rectangle.FromLTRB(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
try
{
foreach (byte[] scanLine in lineIndexes)
{
Marshal.Copy(scanLine, 0,
IntPtr.Add(data.Scan0, data.Stride * rowIndex), width);
if (++rowIndex >= height)
{
break;
}
}
}
finally
{
this.Image.UnlockBits(data);
}
}

Related

Converting System.Drawing Bitmap to Dlib Array2D

In this case, a grayscale Array2D for ShapePredictor.
Here is what I am trying, without much success.
using DlibDotNet;
using Rectangle = System.Drawing.Rectangle;
using System.Runtime.InteropServices;
public static class Extension
{
public static Array2D<byte> ToArray2D(this Bitmap bitmap)
{
var bits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
var length = bits.Stride * bits.Height;
var data = new byte[length];
Marshal.Copy(bits.Scan0, data, 0, length);
bitmap.UnlockBits(bits);
var array = new Array2D<byte>(bitmap.Width, bitmap.Height);
for (var x = 0; x < bitmap.Width; x++)
for (var y = 0; y < bitmap.Height; y++)
{
var offset = x * 4 + y * bitmap.Width * 4;
array[x][y] = data[offset];
}
return array;
}
I've searched and have not yet found a clear answer.
As noted before, you first need to convert your image to grayscale. There are plenty of answers here on StackOverflow to help you with that. I advise the ColorMatrix method used in this answer:
A: Convert an image to grayscale
I'll be using the MakeGrayscale3(Bitmap original) method shown in that answer in my code below.
Typically, images are looped through line by line for processing, so for clarity, you should put your Y loop as outer loop. It also makes the calculation of the data offsets a lot more efficient.
As for the actual data, if the image is grayscale, the R, G and B bytes should all be the same. The order "ARGB" in 32-bit pixel data refers to one UInt32 value, but those are little-endian, meaning the actual order of the bytes is [B, G, R, A]. This means that in each loop iteration we can just take the first of the four bytes, and it'll be the blue component.
public static Array2D<Byte> ToArray2D(this Bitmap bitmap)
{
Int32 stride;
Byte[] data;
// Removes unnecessary getter calls.
Int32 width = bitmap.Width;
Int32 height = bitmap.Height;
// 'using' block to properly dispose temp image.
using (Bitmap grayImage = MakeGrayscale(bitmap))
{
BitmapData bits = grayImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
stride = bits.Stride;
Int32 length = stride*height;
data = new Byte[length];
Marshal.Copy(bits.Scan0, data, 0, length);
grayImage.UnlockBits(bits);
}
// Constructor is (rows, columns), so (height, width)
Array2D<Byte> array = new Array2D<Byte>(height, width);
Int32 offset = 0;
for (Int32 y = 0; y < height; y++)
{
// Offset variable for processing one line
Int32 curOffset = offset;
// Get row in advance
Array2D<Byte>.Row<Byte> curRow = array[y];
for (Int32 x = 0; x < width; x++)
{
curRow[x] = data[curOffset]; // Should be the Blue component.
curOffset += 4;
}
// Stride is the actual data length of one line. No need to calculate that;
// not only is it already given by the BitmapData object, but in some situations
// it may differ from the actual data length. This also saves processing time
// by avoiding multiplications inside each loop.
offset += stride;
}
return array;
}

Marshal.Copy() is not copying to bitmap

I'm working on an image processing project, and I've read that the fastest way to manipulate a bitmap image is to copy it from a byte array using Marshal.Copy(). However, for whatever reason, nothing is being copied from my byte array to my Bitmap, and there's not a clear reason why. This is the code I'm using to copy into my Bitmap:
public void UpdateImage()
{
var data = image.LockBits(
new Rectangle(Point.Empty, image.Size),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(backBuffer, 0, data.Scan0, backBuffer.Length);
image.UnlockBits(data);
Console.WriteLine("UpdateImage");//For debugging purposes
}
I'm attempting to fill the image with complete black, and looking at the data of the backBuffer, it appears to be correct, and as expected, where as when I check any pixel of 'image' it is completely blank. I have no idea why nothing is happening. Any advice would be much appreciated!
Edit: I apologize, I'm a bit new around here, let me provide some more information. Specifically, I am working on some GPU accelerated image processing using Cloo/OpenCL. I wanted to fill the screen with black to make sure that I am doing things correctly, although I am evidently not. Here is the entire class file I'm using:
public class RenderTarget
{
public GraphicsDevice GraphicsDevice;
private byte[] backBuffer;
public Bitmap image;
private ComputeKernel fillKernel;
private ComputeProgram fillProgram;
public void UpdateImage()
{
var data = image.LockBits(
new Rectangle(Point.Empty, image.Size),
ImageLockMode.WriteOnly,
PixelFormat.Format32bppArgb);
Marshal.Copy(backBuffer, 0, data.Scan0, backBuffer.Length);
image.UnlockBits(data);
Console.WriteLine("UpdateImage");
}
//Test method ONLY
public void FillScreen(Color color)
{
if (fillProgram == null)//temporary, all kernels should be compiled on start up. In fact, these probably should be static
{
string fillText = #"
kernel void fillScreen(global uchar* data_out, int from, int to, uchar a, uchar r, uchar g, uchar b){
for (int i = from; i < to; i += 4){
data_out[i] = a;
data_out[i + 1] = r;
data_out[i + 2] = g;
data_out[i + 3] = b;
}
}";
fillProgram = new ComputeProgram(GraphicsDevice.context, fillText);
fillProgram.Build(null, null, null, IntPtr.Zero);
fillKernel = fillProgram.CreateKernel("fillScreen");
}
ComputeBuffer<byte> backBufferBuffer = new ComputeBuffer<byte>(GraphicsDevice.context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.UseHostPointer, backBuffer);
fillKernel.SetMemoryArgument(0, backBufferBuffer);
for (int i = 0; i < backBuffer.Length / 10000; i++)
{
fillKernel.SetValueArgument<int>(1, i * 10000);
fillKernel.SetValueArgument<int>(2, (i + 1) * 10000);
fillKernel.SetValueArgument<byte>(3, color.A);
fillKernel.SetValueArgument<byte>(4, color.R);
fillKernel.SetValueArgument<byte>(5, color.G);
fillKernel.SetValueArgument<byte>(6, color.B);
GraphicsDevice.queue.ExecuteTask(fillKernel, null);
}
GraphicsDevice.queue.ReadFromBuffer(backBufferBuffer, ref backBuffer, false, null);
GraphicsDevice.queue.Finish();
}
public RenderTarget(int Width, int Height, GraphicsDevice device)
{
image = new Bitmap(Width, Height);
backBuffer = new byte[4 * Width * Height];
GraphicsDevice = device;
//Fill the screen with black
FillScreen(Color.Black);
UpdateImage();
Console.WriteLine(image.GetPixel(0, 0).A);
}
}
I have checked to make absolutely sure that the backBuffer is correct. (The values I expected were 255, 0, 0, 0 for the first four elements of the backBuffer).
Okay, I figured out what was going wrong. I had the format in the backBuffer wrong. I was expecting it to be ARGB when it should be ordered RGBA. So, it was a problem with my code in 'fillText'.

libtiff.net writeScanLine returns false

I'm using libtiff.net to make a tiff from a jpeg.
The problem apears when I try to write, because the tiff.writeScanLine returns false, meaning the image isn't written in the tiff.
Why is this happening? And how can I figure out what's wrong?
Here's the code:
private bool creatingTiff()
{
using (Bitmap bmp = new Bitmap(targetFile))
{
using (Tiff tif = Tiff.Open("BitmapTo24BitColorTiff.tif", "w"))
{
byte[] raster = getImageRasterBytes(bmp, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
tif.SetField(TiffTag.IMAGEWIDTH, bmp.Width);
tif.SetField(TiffTag.IMAGELENGTH, bmp.Height);
tif.SetField(TiffTag.COMPRESSION, Compression.OJPEG);
tif.SetField(TiffTag.PHOTOMETRIC, Photometric.YCBCR);
tif.SetField(TiffTag.SUBFILETYPE, 0);
tif.SetField(TiffTag.ROWSPERSTRIP, bmp.Height);
tif.SetField(TiffTag.ORIENTATION, BitMiracle.LibTiff.Classic.Orientation.TOPLEFT);
tif.SetField(TiffTag.XRESOLUTION, bmp.HorizontalResolution);
tif.SetField(TiffTag.YRESOLUTION, bmp.VerticalResolution);
tif.SetField(TiffTag.RESOLUTIONUNIT, ResUnit.INCH);
tif.SetField(TiffTag.BITSPERSAMPLE, 8);
tif.SetField(TiffTag.SAMPLESPERPIXEL, 3);
tif.SetField(TiffTag.JPEGIFOFFSET, 768);
tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
for (int i = 0, offset = 0; i < bmp.Height; i++)
{
bool b = tif.WriteScanline(raster, offset, i, 0);
Console.WriteLine("write succes: " + b);
offset += stride;
}
}
System.Diagnostics.Process.Start("BitmapTo24BitColorTiff.tif");
return true;
}
}
private static byte[] getImageRasterBytes(Bitmap bmp, System.Drawing.Imaging.PixelFormat format)
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
byte[] bits = null;
try
{
// Lock the managed memory
BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
// Declare an array to hold the bytes of the bitmap.
bits = new byte[bmpdata.Stride * bmpdata.Height];
// Copy the values into the array.
System.Runtime.InteropServices.Marshal.Copy(bmpdata.Scan0, bits, 0, bits.Length);
// Release managed memory
bmp.UnlockBits(bmpdata);
}
catch
{
return null;
}
return bits;
}
private static void convertSamples(byte[] data, int width, int height)
{
int stride = data.Length / height;
const int samplesPerPixel = 3;
for (int y = 0; y < height; y++)
{
int offset = stride * y;
int strideEnd = offset + width * samplesPerPixel;
for (int i = offset; i < strideEnd; i += samplesPerPixel)
{
byte temp = data[i + 2];
data[i + 2] = data[i];
data[i] = temp;
}
}
}
The tif-tags are written, but the image itself isn't. Perhaps if someone can point me in the direction of the library developers blog (BitMiracle), I can direct my problem to them directly.
I think your code has the following errors:
You can not use Compression.OJPEG for new images. Old-JPEGs can only be de-compressed.
You probably should not specify TiffTag.JPEGIFOFFSET value by hand. The library will specify proper value itself.
You are trying to write the whole strip using WriteScanline method. You should use WriteEncodedStrip instead.
It also helps to review warnings emitted by the library (it emits them into console).

Sometimes bitmaps are upside down when getting file thumbnails

I use this method to get thumbnails of files (keeping transparency...):
public static Image GetIcon(string fileName, int size)
{
IShellItem shellItem;
Shell32.SHCreateItemFromParsingName(fileName, IntPtr.Zero, Shell32.IShellItem_GUID, out shellItem);
IntPtr hbitmap;
((IShellItemImageFactory)shellItem).GetImage(new SIZE(size, size), 0x0, out hbitmap);
// get the info about the HBITMAP inside the IPictureDisp
DIBSECTION dibsection = new DIBSECTION();
Gdi32.GetObjectDIBSection(hbitmap, Marshal.SizeOf(dibsection), ref dibsection);
int width = dibsection.dsBm.bmWidth;
int height = dibsection.dsBm.bmHeight;
// create the destination Bitmap object
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
unsafe
{
// get a pointer to the raw bits
RGBQUAD* pBits = (RGBQUAD*)(void*)dibsection.dsBm.bmBits;
// copy each pixel manually
for (int x = 0; x < dibsection.dsBmih.biWidth; x++)
{
for (int y = 0; y < dibsection.dsBmih.biHeight; y++)
{
int offset = y * dibsection.dsBmih.biWidth + x;
if (pBits[offset].rgbReserved != 0)
{
bitmap.SetPixel(x, y, Color.FromArgb(pBits[offset].rgbReserved, pBits[offset].rgbRed, pBits[offset].rgbGreen, pBits[offset].rgbBlue));
}
}
}
}
Gdi32.DeleteObject(hbitmap);
return bitmap;
}
But sometimes the image is upside down. When getting the same image for 2nd, 3rd time it's not upside down. Is there any way to determine wether it is upside down or not? If there was any solution, the code below should work:
if (isUpsideDown)
{
int offset = (dibsection.dsBmih.biHeight - y - 1) * dibsection.dsBmih.biWidth + x;
}
else
{
int offset = y * dibsection.dsBmih.biWidth + x;
}
I came across the same problem. Images from the clipboard where upside down. I managed to find out, that you can check the Stride value to see if the image is reversed:
BitmapData d = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
bmp.UnlockBits(d);
if (d.Stride > 0)
{
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
}
If the Stride value is greater than zero, the image is reversed.
Andy

how to read description for the code

hi i send some c# code this is get from imagealg.dll file the class name is diff code is given below how to get description for this code.
public sealed class Diff
{
// Fields
private int diff;
private Bitmap overlayImage;
private Bitmap overlayImage1;
// Methods
public Diff()
{
}
public Diff(Bitmap overlayImage, Bitmap overlayImage1)
{
this.overlayImage = overlayImage;
this.overlayImage1 = overlayImage1;
}
public int Apply(Bitmap srcImg, Bitmap dstImg)
{
int width = srcImg.Width;
int height = srcImg.Height;
int num3 = dstImg.Width;
int num4 = dstImg.Height;
PixelFormat format = (srcImg.PixelFormat == PixelFormat.Format8bppIndexed) ? PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb;
BitmapData data = srcImg.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, format);
BitmapData ovrData = dstImg.LockBits(new Rectangle(0, 0, num3, num4), ImageLockMode.ReadOnly, format);
this.ProcessFilter(data, ovrData, format);
dstImg.UnlockBits(ovrData);
srcImg.UnlockBits(data);
return this.diff;
}
public void ApplyInPlace(Bitmap img)
{
int width = img.Width;
int height = img.Height;
if (((img.PixelFormat != this.overlayImage.PixelFormat) || (width != this.overlayImage.Width)) || (height != this.overlayImage.Height))
{
throw new ArgumentException();
}
if ((img.PixelFormat != PixelFormat.Format8bppIndexed) && (img.PixelFormat != PixelFormat.Format24bppRgb))
{
throw new ArgumentException();
}
BitmapData bitmapdata = img.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
img.UnlockBits(bitmapdata);
}
public int difference()
{
return this.diff;
}
private unsafe void ProcessFilter(BitmapData data, BitmapData ovrData, PixelFormat fmt)
{
int width = data.Width;
int height = data.Height;
int num3 = (fmt == PixelFormat.Format8bppIndexed) ? 1 : 3;
int num4 = width * num3;
int num5 = data.Stride - num4;
byte* numPtr = (byte*) data.Scan0.ToPointer();
byte* numPtr2 = (byte*) ovrData.Scan0.ToPointer();
this.diff = 0;
for (int i = 0; i < height; i++)
{
int num8 = 0;
while (num8 < num4)
{
int num6 = numPtr[0] - numPtr2[0];
string str = Convert.ToString(numPtr[0]);
string str2 = Convert.ToString(numPtr2[0]);
if (num6 != 0)
{
this.diff++;
}
num8++;
numPtr++;
numPtr2++;
}
numPtr += num5;
numPtr2 += num5;
}
}
// Properties
public Bitmap OverlayImage
{
get
{
return this.overlayImage;
}
set
{
this.overlayImage = value;
}
}
}
It sounds like you've recently received and/or been asked to use a library containing code that you don't quite understand. Unfortunately, if the source isn't properly documented, you only have two choices:
Go back to whomever or wherever you got the code from and ask them for proper documentation. Every piece of source code that you receive should come with documentation.
Study the code carefully and try to figure out what it's doing and how you are supposed to use it. This is somewhat closer to "reverse-engineering" the library (except that you have the source), and it's usually reserved as a last-ditch effort.
In fact, if any of us were to try and answer this question, that's all we could do is read and interpret the code you've posted. Is there something specific that you don't understand and are seeking clarification about?
And finally, remember this as a lesson to yourself. Whenever you write code, make sure that you take the time to document it properly so you don't put someone else who tries to use your code in the same situation that you're in now.
You'll be happy to know that the code doesn't do anything particularly significant. The Apply method sets up and calls ProcessFilter, which does a comparison of the two images and returns a count of the number of pixels that differ between the two.
The ApplyInPlace method does nothing.
And I feel sorry for the poor programmer who inherits this code and has to maintain it, what with all those useful variable names like num4 and num6.
It is my fondest wish that the programmer who brought this abomination into the world has been permanently banned from ever touching another keyboard.

Categories