2012-03-13 17 views
7

मैं एक बिटमैप sourceImage.bmpसाथ सी # BitmapData से एक क्षेत्र फसल

है ताला लगा यह बिट्स है:

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

:

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

विश्लेषण करते हैं, एक क्लोन मिल बिट्स अनलॉकिंग:

sourceImage.UnlockBits(dataOriginal); 

क्या यह निर्दिष्ट करना संभव है कि "डेटाऑरिगिनल" का प्रति भाग कॉपी करने के लिए (x, y, w, h)? या डेटा से नया डेटा बनाने के लिए मूल, एक्स और वाई निर्देशांक के साथ-साथ एच और डब्ल्यू निर्दिष्ट करते हैं?

इसका उद्देश्य इस छवि से एक छोटे से क्षेत्र की प्रतिलिपि बनाना है। यह विधि DrawImage से तेज हो सकती है, इसलिए मैं बाद वाले का उपयोग नहीं करता हूं।

संपादित करें:

तो मैं 29 एमबी बिटमैप ले लिया और कुछ कट्टर परीक्षण किया! पूर्ण आकार की फसल (मूल रूप से एक प्रति) + 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 पूर्ण आकार फसल ..) विधि सं। 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 (40ms कम कि 1 Aforge दृष्टिकोण)

परिणाम:

  1. BitmapClone (0 एमएस) ?? (धोखा दे, हैं ना?)
  2. Aforge # 2 (65 एमएस)
  3. Aforge # 1 (105 एमएस)
  4. आयत (170 एमएस)
  5. ताला बिट्स (803 एमएस) (फिक्स के लिए इंतजार/नई परीक्षण के परिणाम ..)
+1

जवाब है हां, लेकिन आप एक समारोह लिखने के लिए क्या करना है, तो अगर आप इसे करना चाहते करने जा रहे हैं जल्दी करो। आपको वांछित आकार का एक नया बिटमैपडाटा बनाना होगा, और बाइट्स के रूप में मूल डेटा पर पुनरावृत्ति करना होगा, एक नई बाइट सरणी में कॉपी करना होगा, जिसे आप नए बिटमैपडेटा में मार्शल कर सकते हैं। – Fopedush

+0

यह सवाल बेहद अंतर्दृष्टिपूर्ण है। मैं एक बड़ी छवि को कई छोटे 8x8 में फसल लगाने की कोशिश कर रहा हूं। ग्राफिक्स.ड्राइमेज() और बिटमैप.क्लोन() विधियां इसके लिए बेहद धीमी हैं। – JBeurer

उत्तर

6

मैंने एक त्वरित (और स्वीकार्य रूप से मोटा) मैनुअल समाधान चाबुक किया जो दर्शाता है कि लॉक बिटमैप्स का उपयोग करके इसे कैसे किया जाए। यह वैकल्पिक तरीकों से काफी तेज़ होना चाहिए, लेकिन इसमें बहुत अधिक कोड शामिल है।जाहिर है

cropped

यदि आप इस कोड का उपयोग करें:

original

उत्पादन करने के लिए इस छवि, 100x100 के लिए क्रॉप @ 15,15:

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

मैं इस मूल छवि का इस्तेमाल किया , आप इसे थोड़ा साफ करना चाहते हैं और त्रुटि प्रबंधन जोड़ना चाहते हैं। अगर मैं आपके प्रश्न को सही तरीके से समझता हूं, तो इस तरह से काम करने से एएफर्ज का उपयोग करने की आवश्यकता को खत्म करना चाहिए।

+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 (डेटाऑरिगिनल, 0, 0, 100, 100);" - कार्यक्रम इस तरह से काम नहीं करते हैं .. आप (बिटमैपडाटा) को (बिटमैप) फ़ील्ड में नहीं डाल सकते हैं, यह विधि DrawImage के समान है। मैं एक ही समय में 30 छवियों को फसल करने की योजना बना रहा हूं। DrawImage और बिटमैप दोनों। क्लोन बहुत सीपीयू उपभोग/धीमी हैं इसलिए मुझे आश्चर्य हुआ कि अगर मैं बस बिटमैपडाटा फसल कर सकता हूं। – Alex

+0

क्षमा करें, आपको बिटमैप उदाहरण पास करना चाहिए और बिटमैपडाटा उदाहरण नहीं होना चाहिए। मैंने यहां इस कोड का परीक्षण किया है और यह ठीक काम करता है, क्योंकि आपके पास कटौती करना चाहते आयताकार आकार के दाहिने आकार पर एक छवि होनी चाहिए। यदि आप बहुत सारी छवियों को फसल करना चाहते हैं तो मैं आपको प्रदर्शन प्राप्त करने के लिए थ्रेड पर स्टार करने की सलाह दूंगा। यदि यह छवि 20x20 पिक्सेल है तो आप 0,0,100,100 में एक इमेज काट नहीं सकते हैं। क्या आप बिटमैप ले सकते हैं और बिटमैपडाटा में प्रवेश कर सकते हैं? आपको बिटमैपडाटा उदाहरण की आवश्यकता क्यों है? –

+0

बिटमैप.क्लोन बहुत तेज है, यदि आप बड़ी छवि लेना चाहते हैं और इसे छोटी 8x8 छवियों में फसल करना चाहते हैं। – JBeurer

2

जब हम मार्शल.copy को memcpy के साथ देखते हैं तो फोपेडश के उत्तर का लाभ बहुत अधिक होता है, क्योंकि इस तरह हमें इसे बाइट [] सरणी के माध्यम से कॉपी करने की आवश्यकता नहीं होती है। इस तरह स्मृति तीन बार की बजाय, केवल एक बार कॉपी हो जाती है!

[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 की जरूरत नहीं है तो मैं तो नहीं डाला है, लेकिन सेशन के परिणामों पर देख कर यह इस की तुलना में धीमी होगी। मैं छवि को आधा में फसल का परीक्षण कर रहा था।

कृपया ध्यान दें, कि अगर हम साथ memcpy का आदान-प्रदान होगा:

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

यह 10x हो जाता है धीमी!

+0

कुछ अजीब कारण से मैं पढ़ने/लिखने की रक्षा की स्मृति के बारे में कुछ कहने memcpy पर AccessViolationException रही। – Kosmos

+0

शायद आपको बीपीपी निरंतर समायोजित करने की आवश्यकता है? मेरे उदाहरण में यह 32 बिट, (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 पढ़ सकते हैं। –

+0

हालांकि यह आपके लिए अच्छा था, इसलिए एक फ्रीलांस कोडिंग समुदाय नहीं है। आपको समझाया जाना चाहिए कि यह ओपी में कैसे मदद करता है और फिर प्रासंगिक कोड प्रदान करता है। बस काम करने वाले कोड चिपकाने से वास्तव में एक सवाल का जवाब नहीं मिलता है क्योंकि यह फ्रीलांस काम प्रदान करता है। – leigero

2

मैं एक नया उपयोगकर्ता हूँ और अभी तक वोट नहीं कर सकते हैं, नहीं तो मैं Korwin80 के जवाब upvoted है | के रूप में यह है, सबसे अधिक कुशल कार्य समाधान प्रदान करता है मेरी राय में। trakos 'समाधान तेजी से निष्पादित हो सकता है लेकिन कम से कम मेरे लिए scrambled छवियों पैदा करता है। यहाँ कैसे मैं Korwin80 के समाधान लागू किया, कुछ मामूली सुधार के साथ, अपने खुद के कोड में है:

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