Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/image/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
c#图像过滤器并行运行需要更长的时间_C#_Image_Visual Studio_Image Processing_Filter - Fatal编程技术网

c#图像过滤器并行运行需要更长的时间

c#图像过滤器并行运行需要更长的时间,c#,image,visual-studio,image-processing,filter,C#,Image,Visual Studio,Image Processing,Filter,这是代码,有没有办法让它更快,因为它的单曲速度较慢 public Bitmap pSetInvert(Bitmap _currentBitmap) { Bitmap temp = (Bitmap)_currentBitmap; Bitmap bmap = (Bitmap)temp.Clone(); Color c; Parallel.For(0, bmap.Width, i => {

这是代码,有没有办法让它更快,因为它的单曲速度较慢

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();
    }
公共位图pSetInvert(位图\u当前位图)
{
位图温度=(位图)\u当前位图;
位图bmap=(位图)临时克隆();
颜色c;
对于(0,bmap.Width,i=>
{
锁(bmap)
{
对于(int j=0;j
并行
中的
。For
将导致代码运行速度比单线程循环慢。锁一次只允许一个线程执行有用的工作,这会增加获取锁的成本

此外,GetPixel和SetPixel的速度非常慢。它们也不能保证线程安全,这可能就是为什么会出现InvalidOperationException

此类型的任何公共静态(在Visual Basic中共享)成员都是线程安全的。任何实例成员都不能保证线程安全

看一看。虽然该类是随WPF引入的,但您可以在各种环境中使用它。我最近在一个控制台应用程序中使用了它。如果需要,WriteableBitmap可以转换为标准位图,也可以写入“.bmp”文件

或者,您也可以使用代码来创建位图缓冲区


如果需要,您可以使用多个线程来执行WriteableBitmap或对位图缓冲区的不安全访问,因为您正在直接读取/写入内存。

这里有两个版本的筛选例程可供使用。它们获取一个
PictureBox
的图像,并在运行过滤器后将其分配给第二个
PictureBox

  • 第一个使用了
    锁位
    ,在我的机器上可以在40秒内对一个ca 400x400映像执行10.000(!)循环
  • 第二个使用
    Parallel.For
    ,除了
    LockBits
    之外,它在35秒内完成相同的工作
当然,这些时间安排受到开销的强烈影响(可能还受到我制造的愚蠢错误的影响),但实际上,使用
锁位是加速的关键,因为“计算”的时间太少了,并行化管理本身占用了大部分核心的时间

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;
}
使用System.Runtime.InteropServices;
// ..
公共无效筛选器1()
{
如果(pictureBox2.Image!=null)pictureBox2.Image.Dispose();
位图bmp=新位图(pictureBox1.Image);
大小s1=bmp.Size;
PixelFormat fmt1=bmp.PixelFormat;
矩形rect=新矩形(0,0,s1.宽度,s1.高度);
BitmapData bmpData=bmp.LockBits(rect,ImageLockMode.ReadWrite,fmt1);

字节bpp=4;//通过使用
lock
可以使代码的并行部分串行工作,这与具有同步开销的单线程应用程序完全相同,因此实际上会更慢

下面是一个帮助器类,它可以直接访问位图数据,并且您可以同时操作图像

FastImage助手类:

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
class的代码:

 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();
            }
        }
公共位图pSetInvert(位图\u当前位图)
{
使用(var bmap=FastImage.Create(_currentBitmap))
{
对于(0,bmap.Width,i=>
{
对于(int j=0;j
阅读您的代码。您希望并行循环中的
锁能做什么?基本上,您的并行程序是无用的如果我不写锁,它会给我系统无效操作异常错误,您会立即在循环体中锁定。这破坏了使用TPL的全部目的,因为所有其他线程都必须等待正在工作的单个线程。@hassan,为了消除锁定,您可以将位图分成多个数组,并分别并行处理这些数组,这样线程之间就不会相互干扰。我只需要准备好的过滤器使其并行,我不做大的东西,请帮助我that@hassan:GetPixel()和SetPixel()根本不是线程安全的。您无法使用这些调用并行处理数据。您可以使用不安全的方法直接访问位图缓冲区(请参阅我答案中的链接)并使用多个线程来处理缓冲区。但是,不安全的线程将比GetPixel/SetPixel快得多,因此您可能不需要额外的线程来处理小图像。它们应该只调用它们
GetPixelReallySlow()
SetPixelEvenSlower()
,也许这样就不会有这么多这样的问题了。@hassan怎么做?你得到了一个很好的答案,只要按照其中的建议去做。@hassan:你不能使用并行。对于GetPixel和SetPixel,它根本不起作用。如果你必须使用并行,你必须使用我概述的其他方法之一来更新位图。对于。我不知道我不知道我可以用多少种不同的方式来表达,所以我只想用这句话来结束我的评论。