Trimming an area from BitmapData using C #

I have a bitmap sourceImage.bmp

bit lock:

BitmapData dataOriginal = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 

Do an analysis, get a clone :

 Bitmap originalClone = AForge.Imaging.Image.Clone(dataOriginal); 

unlock bit:

 sourceImage.UnlockBits(dataOriginal); 

Is it possible to indicate which part of "dataOriginal" to copy (x, y, w, h)? or create new data from the original data by determining the coordinates of X and Y, as well as H and W?

The goal is to copy a small area from this image. This method may be faster than DrawImage, so I do not use the latter.

Edit:

So, I took a 29 MB bitmap and did some hardcore testing! Full-sized crop (mostly copy) + 100 iterations.

http://i.minus.com/ibmcUsT1qUGw6f.png

the code:

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using AForge; using AForge.Imaging; using System.Diagnostics; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; namespace testCropClone { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private unsafe Bitmap Clone(Bitmap bmp, int startX, int startY, int width, int height) { Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); int origByteCount = rawOriginal.Stride * rawOriginal.Height; byte[] origBytes = new Byte[origByteCount]; Marshal.Copy(rawOriginal.Scan0, origBytes, 0, origByteCount); int BPP = 4; //4 Bpp = 32 bits, 3 = 24, etc. byte[] croppedBytes = new Byte[width * height * BPP]; //Iterate the selected area of the original image, and the full area of the new image for (int i = 0; i < height; i++) { for (int j = 0; j < width * BPP; j += BPP) { int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j); int croppedIndex = (i * width * BPP) + (j); //copy data: once for each channel for (int k = 0; k < BPP; k++) { croppedBytes[croppedIndex + k] = origBytes[origIndex + k]; } } } //copy new data into a bitmap Bitmap croppedBitmap = new Bitmap(width, height); BitmapData croppedData = croppedBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(croppedBytes, 0, croppedData.Scan0, croppedBytes.Length); bmp.UnlockBits(rawOriginal); croppedBitmap.UnlockBits(croppedData); return croppedBitmap; } private Bitmap cloneBitmap(Bitmap bmp, int startX, int startY, int width, int height) { Rectangle srcRect = Rectangle.FromLTRB(startX, startY, width, height); Bitmap cloneBitmap = bmp.Clone(srcRect, bmp.PixelFormat); return cloneBitmap; } private Bitmap cloneRectangle(Bitmap bmp, int startX, int startY, int width, int height) { Rectangle srcRect = Rectangle.FromLTRB(startX, startY, width, height); Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height); Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height); using (Graphics graphics = Graphics.FromImage(dest)) { graphics.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel); } return dest; } private Bitmap cloneAforge(Bitmap bmp, int startX, int startY, int width, int height) { BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); Bitmap cloneBitmap = AForge.Imaging.Image.Clone(rawOriginal); bmp.UnlockBits(rawOriginal); return cloneBitmap; } private void button1_Click(object sender, EventArgs e) { Bitmap source = new Bitmap(@"C:\9\01.bmp"); Stopwatch s1 = Stopwatch.StartNew(); for (int i = 0; i < 100; i++) { Bitmap Clone1 = cloneAforge(source, 0, 0, source.Width, source.Height); Clone1.Dispose(); } /*Bitmap Clone1 = cloneAforge(source, 0, 0, source.Width, source.Height); Clone1.Save(@"C:\9\01_aforge.bmp"); Clone1.Dispose();*/ s1.Stop(); source.Dispose(); textBox1.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms"); } private void button2_Click(object sender, EventArgs e) { Bitmap source = new Bitmap(@"C:\9\01.bmp"); Stopwatch s1 = Stopwatch.StartNew(); for (int i = 0; i < 100; i++) { Bitmap Clone1 = cloneBitmap(source, 0, 0, source.Width, source.Height); Clone1.Dispose(); } /*Bitmap Clone1 = cloneBitmap(source, 0, 0, source.Width, source.Height); Clone1.Save(@"C:\9\01_bitmap.bmp"); Clone1.Dispose();*/ s1.Stop(); source.Dispose(); textBox2.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms"); } private void button3_Click(object sender, EventArgs e) { Bitmap source = new Bitmap(@"C:\9\01.bmp"); Stopwatch s1 = Stopwatch.StartNew(); for (int i = 0; i < 100; i++) { Bitmap Clone1 = Clone(source, 0, 0, source.Width, source.Height); Clone1.Dispose(); } /*Bitmap Clone1 = Clone(source, 0, 0, source.Width, source.Height); Clone1.Save(@"C:\9\01_bits.bmp"); Clone1.Dispose();*/ s1.Stop(); source.Dispose(); textBox3.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms"); } private void button4_Click(object sender, EventArgs e) { Bitmap source = new Bitmap(@"C:\9\01.bmp"); Stopwatch s1 = Stopwatch.StartNew(); for (int i = 0; i < 100; i++) { Bitmap Clone1 = cloneRectangle(source, 0, 0, source.Width, source.Height); Clone1.Dispose(); } /*Bitmap Clone1 = cloneRectangle(source, 0, 0, source.Width, source.Height); Clone1.Save(@"C:\9\01_rect.bmp"); Clone1.Dispose();*/ s1.Stop(); source.Dispose(); textBox4.Text = ("" + s1.ElapsedMilliseconds / 100 + " ms"); } } } 

Edit2: (Assumption of a full-sized crop ..) method Nr. 2

  for (int i = 0; i < 100; i++) { Crop crop = new Crop(new Rectangle(0, 0, source.Width, source.Height)); var source2 = crop.Apply(source); source2.Dispose(); } 

Average value = 62 ms (40 ms less than the 1st Afarge approach)

Results:

  • BitmapClone (0 ms) ?? (cheating, right?)
  • Aforge # 2 (65 ms)
  • Aforge # 1 (105 ms)
  • Rectangle (170 ms)
  • Bit lock (803 ms) (waiting for corrections / new test results).
+7
source share
5 answers

I hacked into a quick (and admittedly rude) manual solution that demonstrates how to do this using locked bitmaps. It should be significantly faster than alternative methods, but it includes much more code.

  Bitmap bmp = new Bitmap(@"C:\original.jpg"); Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData rawOriginal = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); int origByteCount = rawOriginal.Stride * rawOriginal.Height; byte[] origBytes = new Byte[origByteCount]; Marshal.Copy(rawOriginal.Scan0, origBytes, 0, origByteCount); //I want to crop a 100x100 section starting at 15, 15. int startX = 15; int startY = 15; int width = 100; int height = 100; int BPP = 4; //4 Bpp = 32 bits, 3 = 24, etc. byte[] croppedBytes = new Byte[width * height * BPP]; //Iterate the selected area of the original image, and the full area of the new image for (int i = 0; i < height; i++) { for (int j = 0; j < width * BPP; j += BPP) { int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j); int croppedIndex = (i * width * BPP) + (j); //copy data: once for each channel for (int k = 0; k < BPP; k++) { croppedBytes[croppedIndex + k] = origBytes[origIndex + k]; } } } //copy new data into a bitmap Bitmap croppedBitmap = new Bitmap(width, height); BitmapData croppedData = croppedBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(croppedBytes, 0, croppedData.Scan0, croppedBytes.Length); bmp.UnlockBits(rawOriginal); croppedBitmap.UnlockBits(croppedData); croppedBitmap.Save(@"C:\test.bmp"); 

I used this original image:

original

To display this image, cropped to 100x100 @ 15.15:

cropped

Obviously, if you use this code, you need to clean it up a bit and add error handling. If I understand your question correctly, doing it this way should eliminate the need to use AForge at all.

+6
source

You can try something like this:

 public static Bitmap CropBitmap(Bitmap bitmap, int x, int y, int w, int h) { Rectangle rect = new Rectangle(x, y, w, h); Bitmap cropped = bitmap.Clone(rect, bitmap.PixelFormat); return cropped; } 

And do something like this in yout (sample) code:

 var croppedImagem = CropBitmap(dataOriginal, 0, 0, 100, 100); 

Hope this helps!

+2
source

Fopedush's answer is very useful when we provide Marshal.copy with memcpy because we do not need to copy it through the byte [] array. Thus, the memory is copied only once, not three times!

 [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] static unsafe extern int memcpy(byte* dest, byte* src, long count); static public Bitmap cropBitmap(Bitmap sourceImage, Rectangle rectangle) { const int BPP = 4; //4 Bpp = 32 bits; argb var sourceBitmapdata = sourceImage.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var croppedImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format32bppArgb); var croppedBitmapData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); unsafe { croppedBitmapData.Stride = sourceBitmapdata.Stride; byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer(); byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer(); memcpy(croppedImagePointer, sourceImagePointer, Math.Abs(croppedBitmapData.Stride) * rectangle.Height); } sourceImage.UnlockBits(sourceBitmapdata); croppedImage.UnlockBits(croppedBitmapData); return croppedImage; } 

My results:

 BitmapClone: 1823 ms LockBits: 4857 ms Rectangle: 1479 ms My method: 559 ms My method with LockBits on source image done only once (before loop): 160 ms 

I don't have AForge, so I didn't turn it on, but looking at the op results, it will be slower than that. I tested cropping the image in half.

Please note that if we exchange memcpy for:

 for (int k = 0; k < Math.Abs(croppedBitmapData.Stride) * rectangle.Height; k++) *(croppedImagePointer++) = *(sourceImagePointer++); 

he gets 10 times slower!

+2
source

I am a new user and I can’t vote, otherwise I would support Korwin80 , because it provides the most effective working solution, in my opinion, trakos ' solution can be faster, but it gives scrambled images, at least for me. Here's how I applied the Korwin80 solution with some minor improvements in my own code:

 [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] private unsafe static extern int memcpy(byte* dest, byte* src, long count); private unsafe Bitmap Crop(Bitmap srcImg, Rectangle rectangle) { if ((srcImg.Width == rectangle.Width) && (srcImg.Height == rectangle.Height)) return srcImg; var srcImgBitmapData = srcImg.LockBits(new Rectangle(0, 0, srcImg.Width, srcImg.Height), ImageLockMode.ReadOnly, srcImg.PixelFormat); var bpp = srcImgBitmapData.Stride / srcImgBitmapData.Width; // 3 or 4 var srcPtr = (byte*)srcImgBitmapData.Scan0.ToPointer() + rectangle.Y * srcImgBitmapData.Stride + rectangle.X * bpp; var srcStride = srcImgBitmapData.Stride; var dstImg = new Bitmap(rectangle.Width, rectangle.Height, srcImg.PixelFormat); var dstImgBitmapData = dstImg.LockBits(new Rectangle(0, 0, dstImg.Width, dstImg.Height), ImageLockMode.WriteOnly, dstImg.PixelFormat); var dstPtr = (byte*)dstImgBitmapData.Scan0.ToPointer(); var dstStride = dstImgBitmapData.Stride; for (int y = 0; y < rectangle.Height; y++) { memcpy(dstPtr, srcPtr, dstStride); srcPtr += srcStride; dstPtr += dstStride; } srcImg.UnlockBits(srcImgBitmapData); dstImg.UnlockBits(dstImgBitmapData); return dstImg; } 
+2
source

this class gets your raster object. then lobbits. to ctor. When you call the cropping method, it uses memcpy to copy the desired area to the new bmp.

lockbits : tells the garbage collector NOT to move my bits anywhere because I'm going to change it with pointers (scan0).

memcpy : the fastest copy. can copy blocks of memory. optimized by some experts.

why is memcpy fast?

instead of copying byte by bit, (widthheight) memory access time. memcpy does this block by block, much more than wh times.

 internal unsafe sealed class FastImageCroper : IDisposable { private readonly Bitmap _srcImg; private readonly BitmapData _srcImgBitmapData; private readonly int _bpp; private readonly byte* _srtPrt; public FastImageCroper(Bitmap srcImg) { _srcImg = srcImg; _srcImgBitmapData = srcImg.LockBits(new Rectangle(0, 0, srcImg.Width, srcImg.Height), ImageLockMode.ReadOnly, srcImg.PixelFormat); _bpp = _srcImgBitmapData.Stride / _srcImgBitmapData.Width; // == 4 _srtPrt = (byte*)_srcImgBitmapData.Scan0.ToPointer(); } public Bitmap Crop(Rectangle rectangle) { Bitmap dstImg = new Bitmap(rectangle.Width, rectangle.Height, _srcImg.PixelFormat); BitmapData dstImgBitmapData = dstImg.LockBits(new Rectangle(0, 0, dstImg.Width, dstImg.Height), ImageLockMode.WriteOnly, dstImg.PixelFormat); byte* dstPrt = (byte*)dstImgBitmapData.Scan0.ToPointer(); byte* srcPrt = _srtPrt + rectangle.Y*_srcImgBitmapData.Stride + rectangle.X*_bpp; for (int y = 0; y < rectangle.Height; y++) { int srcIndex = y * _srcImgBitmapData.Stride; int croppedIndex = y * dstImgBitmapData.Stride; memcpy(dstPrt + croppedIndex, srcPrt + srcIndex, dstImgBitmapData.Stride); } dstImg.UnlockBits(dstImgBitmapData); return dstImg; } public void Dispose() { _srcImg.UnlockBits(_srcImgBitmapData); } [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] private static extern int memcpy(byte* dest, byte* src, long count); } 
+1
source

All Articles