zoukankan      html  css  js  c++  java
  • 【转】【WPF】wpf 图片指针处理

    我一直用GDI+做Winform 的基于指针的图片处理,这次下决心全部移到wpf上(主要是显示布局很方便)
    采用的图片是
    2512*3307 的大图 830万像素
    类库基于WritableBitmapEx 的wpf版本
    函数是我自己写的扩展方法,只是利用了 writableBitmapEx提供的环境 ,我懒得从头到尾自己写了
     
    1.标准int32数组遍历计算 release
    0.28s

    public unsafe static void TestGray1(WriteableBitmap bmp)
           {
                using (var context = bmp.GetBitmapContext())
                {
                    int height = context.Height;
                    int width = context.Width;
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            int pos = y * context.Width + x;
                            var c = context.Pixels[pos];
                            var r = (byte)(c >> 16);
                            var g = (byte)(c >> 8);
                            var b = (byte)(c);
    
                            var gray = ((r * 38 + g * 75 + b * 15) >> 7);
    
                            var color = (255 << 24) | (gray << 16) | (gray << 8) | gray;
                            context.Pixels[pos] = color;
                        }
                    }
                }
            }

    2.标准int32指针遍历计算 release

    0.04s

    public unsafe static void TestGray2(WriteableBitmap bmp)
            {
                using (var context = bmp.GetBitmapContext())
                {
                    var ptr = context.Pixels;
                    int height = context.Height;
                    int width = context.Width;
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            var c = *ptr;
                            var r = (byte)(c >> 16);
                            var g = (byte)(c >> 8);
                            var b = (byte)(c);
    
                            var gray = ((r * 38 + g * 75 + b * 15) >> 7);
    
                            var color = (255 << 24) | (gray << 16) | (gray << 8) | gray;
                            *ptr = color;
    
                            ptr++;
                        }
                    }
                }
            }

    3.colorstruct指针 遍历计算

    0.02 s

    应该是已经到极限速度了[除了后面的并行方式],我已经想不出还有什么方法可以提高处理速度

    而且这种方式是最直观的,最容易理解的处理方式,也便于以后维护

    [StructLayout(LayoutKind.Sequential)]
        public struct PixelColor
        {
            public byte Blue;
            public byte Green;
            public byte Red;
            public byte Alpha;
        }
    public unsafe static void TestGray3(WriteableBitmap bmp)
            {
                using (var context = bmp.GetBitmapContext())
                {
                    var ptr = (PixelColor*)context.Pixels;
    
                    int height = context.Height;
                    int width = context.Width;
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            var c = *ptr;
                            var gray = ((c.Red * 38 + c.Green * 75 + c.Blue * 15) >> 7);
                            (*ptr).Green=(*ptr).Red=(*ptr).Blue = (byte)gray;
    
                            ptr++;
                        }
                    }
                }
            }

    4.作为对比,我又测试了一下 GDI+的 指针处理图片的速度

    0.06s

    public static unsafe Bitmap ToGray(Bitmap img)
            {
                var rect = new System.Drawing.Rectangle(0, 0, img.Width, img.Height);
                var data = img.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                var ptr = (ColorType*)data.Scan0.ToPointer();
                var bytes = new Int32[img.Width * img.Height];
                var height = img.Height;
                var width = img.Width;
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        var color = *ptr;
                        var gray = ((color.R * 38 + color.G * 75 + color.B * 15) >> 7);
    
                        (*ptr).R = (*ptr).G = (*ptr).B = (byte)gray;
    
                        ptr++;
                    }
                }
                img.UnlockBits(data);
                return img;
            }

    5.重头戏来了。我一直对Parallel.For 很迷惑,为什么他的消耗时间是普通for的好几倍。今天仔细研究了一下,发现原来是用错了

    0.01秒   release

     笔记本i5cpu,如果台式机的I7会更加强悍,速度会成半成半降低。

    主要是利用了微软的任务并行库的循环并行化的方法。

    注意:默认的并行循环对于函数体很小的情况是很慢的,这种情况必须用Partitioner 创建循环体,这在MSDN有介绍,是关键之中的关键

    public  unsafe static void TestGray5(WriteableBitmap bmp)
            { 
                using (var context = bmp.GetBitmapContext())
                {
                    int height = context.Height;
                    int width = context.Width;
    
                    Parallel.ForEach(Partitioner.Create(0, height), (h) =>
                    {
                        var ptr = (PixelColor*)context.Pixels;
                        ptr += h.Item1 * width;
    
                        for (int y = h.Item1; y < h.Item2; y++)
                        {
                            for (int x = 0; x < width; x++)
                            {
                                var c = *ptr;
                                var gray = ((c.Red * 38 + c.Green * 75 + c.Blue * 15) >> 7);
                                (*ptr).Green = (*ptr).Red = (*ptr).Blue = (byte)gray;
    
                                ptr++;
                            }
                        }
    
                    });
    
                }
            }

    感想

    1.绝对不要在循环体内使用属性或函数,很有可能会降低数倍计算速度。

    因为属性本质上是个函数,而在循环体内最好不要再调用函数,如果确实需要用内联代码的方式,c#没有inline,那么copy代码吧,反正为了速度。

    2. 用指针移位操作 似乎比 直接数组访问要快10倍啊

    我感觉要么是cache命中的原因,要么是 数组本身存取被属性封装了。相当于又调用了函数。

    3.TPL 任务并行库果真好用,看来微软早已考虑过大量数据并行的循环优化问题09年,只是我一直用错了方法,才觉得很慢。

    摘自 苦力熊

    原文地址:http://www.2cto.com/kf/201204/129454.html

  • 相关阅读:
    基于NS2的差分服务网络测试(含awk分析代码)
    ubuntu14下NSG2的安装和使用
    (转)QOS入门详解
    gnuplot安装的小问题
    中介者模式(Mediator)_java实现
    命令模式(Command)_java实现
    观察者模式(Observer)_java实现
    策略模式(Strategy)_java实现
    状态模式(State)_java实现
    备忘录模式(Memento)_java实现
  • 原文地址:https://www.cnblogs.com/mqxs/p/3770249.html
Copyright © 2011-2022 走看看