2012-03-13 1 views
7

У меня есть растровое sourceImage.bmpотрезания области от BitmapData с C#

замок это биты:

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

ли анализ, получить клон:

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

разблокировки:

sourceImage.UnlockBits(dataOriginal); 

Можно ли указать, какую часть «dataOriginal» нужно скопировать (x, y, w, h)? или создавать новые данные из данныхОригинал, определяя координаты X и Y, а также H и W?

Цель состоит в том, чтобы скопировать небольшую область с этого изображения. Этот метод может быть быстрее, чем DrawImage, поэтому я не использую последний.

Edit:

Так что я взял 29 Mb растровые и сделал некоторые испытания хардкора! Полноразмерный урожай (в основном копия) + 100 итераций.

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

Код:

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: (Aforge полноразмерная Crop ..) способ 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(); 

     } 

Среднее = 62ms (40 мс меньше, чем первое Aforge подход)

Результаты:

  1. BitmapClone (0 мс) ?? (Мошенничество, не так ли?)
  2. Aforge # 2 (65 мс)
  3. Aforge # 1 (105 мс)
  4. прямоугольник (170 мс)
  5. биты блокировки (803 мс) (ожидание исправлений/новые результаты испытаний ..)
+1

Ответ: да, но вы будете иметь, чтобы написать функцию, чтобы делать, если если вы хотите его быть быстрым. Вам нужно будет создать новую bitmapdata нужного размера и перебрать исходные данные в виде байтов, скопировав их в новый массив байтов, который затем вы можете перевести в новую bitmapData. – Fopedush

+0

Этот вопрос очень проницателен. Я пытаюсь создать большое изображение во многих небольших 8x8. Для этого чрезвычайно медленны методы Graphics.DrawImage() и Bitmap.Clone(). – JBeurer

ответ

6

Я быстро взломал (и, по общему признанию, грубое) решение, которое демонстрирует, как это сделать, используя заблокированные растровые изображения. Он должен быть значительно быстрее, чем альтернативные методы, но требует гораздо больше кода.

 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"); 

Я использовал этот оригинальный образ:

original

Чтобы вывести это изображение, обрезается до 100x100 @ 15,15:

cropped

Очевидно, что если вы используете этот код , вам нужно немного почистить его и добавить обработку ошибок. Если я правильно понимаю ваш вопрос, то все это должно устранить необходимость использования AForge.

+1

Прежде всего, спасибо за ваше время и силы, написав этот код! Во-вторых, я провел некоторое тестирование, но, к сожалению, это решение имеет худшие результаты! ;) не стесняйтесь критиковать мой подход к тестированию. Возможно, я совершил ошибку или два.(см. Редактировать) – Alex

+0

Интересно, я этого не ожидал. Я сделаю небольшое тестирование и посмотрю, согласуются ли мои результаты с вашими. – Fopedush

+0

было бы здорово! Я попытался максимально упростить тестовую инфраструктуру, чтобы исключить серьезные ошибки (полный код в редакторе), но, возможно, вы получите другие результаты с использованием другого подхода. С нетерпением жду этого! – Alex

2

Вы можете попробовать что-то вроде этого:

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; 
} 

И сделать что-то подобное в YOUT коде (пример):

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

Надеюсь, это поможет!

+1

"var croppedImagem = CropBitmap (dataOriginal, 0, 0, 100, 100);" - программы не работают таким образом .. вы не можете поместить (BitmapData) в поле (Bitmap), также этот метод похож на DrawImage. Я планирую вырезать до 30 изображений одновременно. И DrawImage, и Bitmap.Clone очень сильно потребляют процессор/медленно, поэтому я подумал, могу ли я просто обрезать BitmapData. – Alex

+0

Извините, вы должны передать экземпляр Bitmap, а не экземпляр BitmapData. Я протестировал этот код здесь, и он отлично работает, у вас должно быть изображение с правильным размером прямоугольника, который вы хотите вырезать. Если вы хотите создать множество изображений, я бы порекомендовал вам поиграть в нить, чтобы получить производительность. Вы не можете вырезать изображение в размере 0,0100,100, если это изображение составляет 20x20 пикселей. Можете ли вы взять Bitmap и ingnore BitmapData? Зачем нужен экземпляр BitmapData? –

+0

Bitmap.Clone ОЧЕНЬ, ОЧЕНЬ медленный, если вы хотите сделать большое изображение и обрезать его в маленькие 8x8 изображения. – JBeurer

2

Ответ Fopedush очень полезен, когда мы предоставляем Marshal.copy с memcpy, потому что нам не нужно копировать его через массив byte []. Таким образом, память копируется только один раз, а не три раза!

[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; 
} 

Мои результаты:

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 

Я не AForge, так что я не включил, но, посмотрев на результаты Op в это будет медленнее, чем это. Я тестировал обрезку изображения пополам.

Пожалуйста, обратите внимание, что если бы мы обменивать тетсру с:

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

он получает в 10 раз медленнее!

+0

По какой-то странной причине я получаю AccessViolationException в memcpy, говоря что-то о чтении/записи в защищенную память. – Kosmos

+0

Возможно, вам нужно настроить константу BPP? В моем примере предполагается, что 32bit, (argb), для использования 24bit (rgb) 3. AccessViolationException означает, что вы пытаетесь получить доступ к памяти за пределами. – trakos

+0

Я не думаю, что это поможет, поскольку он назначен, но никогда не используется. – Kosmos

1
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

Добро пожаловать в переполнение стека. Возможно, вы можете сначала прочитать http://stackoverflow.com/help/how-to-answer, чтобы ваш ответ соответствовал стандартам SO. –

+0

Хотя это было мило с вашей стороны, SO не является независимым кодирующим сообществом. Вы должны объяснить, как это помогает OP, а затем предоставлять соответствующий код. Просто вставить рабочий код на самом деле не отвечает на вопрос, так как он обеспечивает внештатную работу. – leigero

2

Я новый пользователь и еще не может голосовать, иначе я бы upvoted ответ Korwin80 «s, поскольку он обеспечивает наиболее эффективное рабочее решение, на мой взгляд. trakos «решение может выполняться быстрее, но дает скремблированные изображения, по крайней мере для меня. Вот как я применил решение Korwin80 «S, с некоторыми незначительными улучшениями, в моем собственном коде:

[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; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^