обрезать область от битмапдата с 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);
.
Цель - скопировать небольшую область из этого изображения. Этот метод может быть быстрее, чем увеличение, поэтому я не использую последние.
Редактировать:
Итак, я взял 29 mb растровое изображение и сделал некоторые хардкорные тестирования! Полноразмерная культура (в основном копия) + 100 итераций.
код:
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");
}
}
}
.
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();
}
.
Средний= 62 мс (40 мс меньше, чем 1-й последующий подход)
<Сильные> Результаты:
- bitmapclone (0 мс) ?? (Обман, не так ли?)
- aforge # 2 (65 мс)
- aforge # 1 (105 мс)
- прямоугольник (170 мс)
- Блокировка бит (803 мс) (ожидание исправлений / новых результатов теста ..)
Решение
Я взбил быстрое (и по общему признанию грубого) ручного решения, который демонстрирует, как сделать это, используя заблокированные растровые изображения.Это должно быть значительно быстрее, чем альтернативные методы, но включает в себя гораздо больше кода.
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");
.
Я использовал это оригинальное изображение:
Для вывода этого изображения обрезается до 100x100 @ 15,15:
Очевидно, что если вы используете этот код, вы захотите немного очистить его и добавить обработку ошибок.Если я правильно понимаю ваш вопрос, делая вещи таким образом, следует устранить необходимость использования Aforge вообще.
Другие советы
Ответ Fopedush в значительной степени пользуется, когда мы подрядим Marshal.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 медленнее!
Вы можете попробовать что-то вроде этого:
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);
.
Я надеюсь, что это поможет!
Я новый пользователь и не могу проголосовать еще, иначе я бы усвоил korwin80 ответ, как он обеспечивает наиболее эффективное рабочее решение,на мой взгляд. Решение Trakos может выполняться быстрее, но дает зашифрованные изображения, по крайней мере для меня.Вот как я поступил 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;
}
. Этот класс получает ваш растровый Obj.затем локоблоки.в CTOR.
Когда вы называете метод обрезки, он использует
lockbits : рассказывает сборщик мусора, чтобы не перемещать мои биты в любом месте, потому что я собираюсь изменить его по указателям (Scan0).
<Сильные> Мемкпи : Самая быстрая копия.можно скопировать блоки памяти.оптимизирован некоторыми экспертами.
Почему memcpy быстро?
вместо копирования байта by byte, (ширина высота) раз доступ памяти. memcpy делает его блоком по блоку, гораздо меньше, чем w h раз.
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);
}
.