使用C#从BitmapData裁剪区域

Ale*_*lex 8 c# image bitmap aforge

我有一个位图sourceImage.bmp

锁定它的位:

BitmapData dataOriginal = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Run Code Online (Sandbox Code Playgroud)

做分析,得到一个克隆:

Bitmap originalClone = AForge.Imaging.Image.Clone(dataOriginal);
Run Code Online (Sandbox Code Playgroud)

解锁位:

sourceImage.UnlockBits(dataOriginal);
Run Code Online (Sandbox Code Playgroud)

是否可以指定要复制的"dataOriginal"的哪一部分(x,y,w,h)?或者从dataOriginal创建新数据,指定X和Y坐标以及H和W?

目的是从该图像中复制一个小区域.这个方法可能比DrawImage更快,这就是我不使用后者的原因.

编辑:

所以我拿了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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Edit2 :( Aforge全尺寸裁剪..)方法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();

        }
Run Code Online (Sandbox Code Playgroud)

平均值= 62ms(比第一个Aforge方法少40ms)

结果:

  1. BitmapClone(0毫秒)?? (作弊,不是吗?)
  2. Aforge#2(65毫秒)
  3. Aforge#1(105毫秒)
  4. 矩形(170毫秒)
  5. 锁定位(803 ms)(等待修复/新测试结果..)

Fop*_*ush 8

我掀起了一个快速(并且粗略地说)粗略的手动解决方案,该解决方案演示了如何使用锁定的位图执行此操作.它应该比替代方法快得多,但确实涉及更多代码.

        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");
Run Code Online (Sandbox Code Playgroud)

我使用了这个原始图像:

原版的

要输出此图像,裁剪为100x100 @ 15,15:

裁剪

显然,如果您使用此代码,您将需要稍微清理它并添加错误处理.如果我理解你的问题,那么按照这种方式做事就不需要使用AForge了.

  • 对于那些想要使用此代码的人:将 ```int origIndex = (startX * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startY * BPP) + (j);``` 更改为 ``` int origIndex = (startY * rawOriginal.Stride) + (i * rawOriginal.Stride) + (startX * BPP) + (j);``` (2认同)