这里的代码是否有任何方法可以使其更快,因为它更慢的单一
public Bitmap pSetInvert(Bitmap _currentBitmap)
{
Bitmap temp = (Bitmap)_currentBitmap;
Bitmap bmap = (Bitmap)temp.Clone();
Color c;
Parallel.For(0, bmap.Width, i =>
{
lock (bmap)
{
for (int j = 0; j < bmap.Height; j++)
{
c = bmap.GetPixel(i, j);
bmap.SetPixel(i, j, Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B));
}
}
});
return (Bitmap)bmap.Clone();
}
答案 0 :(得分:2)
通过使用lock
,您可以使代码的并行部分串行工作,就像具有同步开销的单个线程应用程序一样,实际上它会更慢。
这是一个直接访问位图数据的辅助类,您可以同时操作图像。
FastImage Helper Class:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
namespace ImageManipulation
{
class FastImage : IDisposable
{
private Bitmap _buffer;
private byte[] _rawData;
private GCHandle _rawHandle;
private int _formatSize;
private int _width;
private int _height;
public int Width
{
get { return _width; }
}
public int Height
{
get { return _height; }
}
public byte[] GetRawData()
{
return _rawData;
}
public byte this[int index]
{
get { return _rawData[index]; }
set { _rawData[index] = value; }
}
public Color this[int x, int y]
{
get
{
return GetPixel(x, y);
}
set
{
SetPixel(x, y, value);
}
}
public Color GetPixel(int x, int y)
{
var offset = y*_width*_formatSize;
offset += x*_formatSize;
return Color.FromArgb(_rawData[offset + 3], _rawData[offset + 2], _rawData[offset + 1], _rawData[offset]);
}
public void SetPixel(int x, int y, Color value)
{
var offset = y*_width*_formatSize;
offset += x*_formatSize;
_rawData[offset] = value.B;
_rawData[offset + 1] = value.G;
_rawData[offset + 2] = value.R;
_rawData[offset + 3] = value.A;
}
private FastImage() { }
public static FastImage Create(Image source)
{
var image = new FastImage();
var bmpSource = new Bitmap(source);
var bmpData = bmpSource.LockBits(new Rectangle(0, 0, source.Width, source.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmpSource.PixelFormat);
image._width = source.Width;
image._height = source.Height;
image._formatSize = 4;
var stride = bmpSource.Width * image._formatSize;
image._rawData = new byte[stride * bmpSource.Height];
image._rawHandle = GCHandle.Alloc(image._rawData, GCHandleType.Pinned);
var pointer = Marshal.UnsafeAddrOfPinnedArrayElement(image._rawData, 0);
image._buffer = new Bitmap(bmpSource.Width, bmpSource.Height, stride, PixelFormat.Format32bppArgb /*bmpSource.PixelFormat*/, pointer);
bmpSource.UnlockBits(bmpData);
var graphics = Graphics.FromImage(image._buffer);
graphics.DrawImageUnscaledAndClipped(bmpSource, new Rectangle(0, 0, source.Width, source.Height));
graphics.Dispose();
return image;
}
public void Dispose()
{
_rawHandle.Free();
_buffer.Dispose();
}
public void Save(Stream stream)
{
_buffer.Save(stream, ImageFormat.Bmp);
}
public Bitmap ToBitmap()
{
return (Bitmap)_buffer.Clone();
}
}
}
以下是使用FastImage
类的代码:
public Bitmap pSetInvert(Bitmap _currentBitmap)
{
using (var bmap = FastImage.Create(_currentBitmap))
{
Parallel.For(0, bmap.Width, i =>
{
for (int j = 0; j < bmap.Height; j++)
{
var c = bmap.GetPixel(i, j);
bmap.SetPixel(i, j, Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B));
}
});
return bmap.ToBitmap();
}
}
答案 1 :(得分:1)
lock
中的Parallel.For
将导致代码运行速度慢于单线程循环。锁只允许一次一个线程做有用的工作,增加了获取锁的成本。
此外,GetPixel和SetPixel 非常慢。它们也不保证是线程安全的,这可能是你获得InvalidOperationException的原因
此类型的任何公共静态(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。
http://msdn.microsoft.com/en-us/library/System.Drawing.Bitmap(v=vs.110).aspx
请改为WriteableBitmap。尽管该类是随WPF引入的,但您可以在各种环境中使用它。我最近在控制台应用程序中使用它。如果需要,可以将WriteableBitmap转换为标准位图,或者将其写入“.bmp”文件。
或者,您可以使用unsafe代码来directly access位图缓冲区。
如果需要,可以使用多个线程进行WriteableBitmap或不安全地访问Bitmap缓冲区,因为您直接读/写内存。
答案 2 :(得分:1)
以下是您可以使用的两个版本的过滤器例程。他们拍摄一张PictureBox
的图片,然后在浏览过滤器后将其分配给第二个PictureBox
。
LockBits
并且可以在我的机器上在40秒内为一个400x400图像执行10.000(!)循环。Parallel.For
之外,第二个使用LockBits
并在35秒内执行相同的工作。当然,这些时间很大程度上受到开销的影响(也许是我所做的愚蠢错误。)但实际上,使用Lockbits
是提高速度的关键,因为有很少的计算和计算。并行化管理本身会占用大部分内核。时间..
using System.Runtime.InteropServices;
// ..
public void filter1()
{
if (pictureBox2.Image != null) pictureBox2.Image.Dispose();
Bitmap bmp = new Bitmap(pictureBox1.Image);
Size s1 = bmp.Size;
PixelFormat fmt1 = bmp.PixelFormat;
Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, fmt1);
byte bpp = 4; // <<-------------------set to 3 for 24bbp !!
int size1 = bmpData.Stride * bmpData.Height;
byte[] data = new byte[size1];
Marshal.Copy(bmpData.Scan0, data, 0, size1);
for (int y = 0; y < s1.Height; y++)
{
for (int x = 0; x < s1.Width; x++)
{
int index = y * bmpData.Stride + x * bpp;
data[index + 0] = (byte) (255 - data[index + 0]); // Blue
data[index + 1] = (byte) (255 - data[index + 1]); // Green
data[index + 2] = (byte) (255 - data[index + 2]); // Red
data[index + 3] = 255; // Alpha, comment out for 24 bpp!
}
}
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
pictureBox2.Image = bmp;
}
public void filter2()
{
if (pictureBox2.Image != null) pictureBox2.Image.Dispose();
Bitmap bmp1 = new Bitmap(pictureBox1.Image);
Size s1 = bmp1.Size;
Bitmap bmp2 = new Bitmap(s1.Width, s1.Height);
PixelFormat fmt1 = bmp1.PixelFormat;
Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
BitmapData bmpData1 = bmp1.LockBits(rect, ImageLockMode.ReadOnly, fmt1);
BitmapData bmpData2 = bmp2.LockBits(rect, ImageLockMode.WriteOnly, fmt1);
byte bpp = 4; // set to 3 for 24bbp !!
int size1 = bmpData1.Stride * bmpData1.Height;
byte[] data1 = new byte[size1];
byte[] data2 = new byte[size1];
Marshal.Copy(bmpData1.Scan0, data1, 0, size1);
Marshal.Copy(bmpData2.Scan0, data2, 0, size1);
int degreeOfParallelism = Environment.ProcessorCount - 1;
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = degreeOfParallelism;
Parallel.For(0, bmp1.Width, options, y =>
{
{
for (int x = 0; x < s1.Width; x++)
{
int index = y * bmpData1.Stride + x * bpp;
data2[index + 0] = (byte)(255 - data1[index + 0]); // Blue
data2[index + 1] = (byte)(255 - data1[index + 1]); // Green
data2[index + 2] = (byte)(255 - data1[index + 2]); // Red
data2[index + 3] = 255; // Alpha, comment out for 24 bpp!
}
}
});
Marshal.Copy(data2, 0, bmpData2.Scan0, data2.Length);
bmp1.UnlockBits(bmpData1);
bmp2.UnlockBits(bmpData2);
pictureBox2.Image = bmp2;
}
请注意,第二个过滤器例程需要处理第二个Bitmap以用于通用过滤器算法。这个简单的反演滤波器并不是真的需要它,但是一旦你编写了访问相邻像素的东西,比如像你那样的模糊滤镜。
还要注意通道字节的顺序!