如何在MonoTouch中优化此图像卷积滤镜方法?

时间:2011-10-02 09:29:23

标签: c# uiimage xamarin.ios graphic convolution

在意识到MonoTouch不存在实时图形效果库之后,我决定自己编写。经过一些研究,我写了一个完美的卷积方法,但即使使用不安全的代码,也非常慢。 我做错了什么?是否有一些我缺少的优化?

这是我的c#课程,任何建议,无论多小,都欢迎!

using System;
using System.Drawing;
using MonoTouch.CoreGraphics;
using System.Runtime.InteropServices;
using MonoTouch.UIKit;
using MonoTouch;

namespace FilterLibrary
{

public class ConvMatrix
{

    public int Factor { get; set; }
    public int Offset { get; set; }

    private int[,] _matrix = {  {0, 0, 0, 0, 0},
                                {0, 0, 0, 0, 0},
                                {0, 0, 1, 0, 0},
                                {0, 0, 0, 0, 0},
                                {0, 0, 0, 0, 0}
                            };

    public int[,] Matrix
    {
        get { return _matrix; }
        set
        {
            _matrix = value;

            Factor = 0;
            for (int i = 0; i < Size; i++)
                for (int j = 0; j < Size; j++)
                    Factor += _matrix[i, j];

            if (Factor == 0)
                Factor = 1;
        }
    }

    private int _size = 5;
    public int Size
    {
        get { return _size; }
        set
        {
            if (value != 1 && value != 3 && value != 5 && value != 7)
                _size = 5;
            else
                _size = value;
        }
    }

    public ConvMatrix()
    {
        Offset = 0;
        Factor = 1;
    }
}

    public class ConvolutionFilter
{
    public ConvolutionFilter ()
    {
    }

public static CGImage GaussianSmooth (CGImage image)
    {
        ConvMatrix matr = new ConvMatrix ();
        matr.Matrix = new int[5, 5] {
                                { 1 , 4 , 7 , 4 , 1 },
                                { 4 ,16 ,26 ,16 , 4 },
                                { 7 ,26 ,41 ,26 , 7 },
                                { 4 ,16 ,26 ,16 , 4 },
                                { 1 , 4 , 7 , 4 , 1 }
                                };



        return Filter.ImageConvolution (image, matr);

    }


    public static CGImage MotionBlur (CGImage image)
    {
        ConvMatrix matr = new ConvMatrix ();
        matr.Size = 7;
        matr.Matrix = new int[7, 7] {
                                        { 1 , 0 , 0 , 0 , 0 , 0 , 0},
                                        { 0 , 1 , 0 , 0 , 0 , 0 , 0},
                                        { 0 , 0 , 1 , 0 , 0 , 0 , 0},
                                        { 0 , 0 , 0 , 1 , 0 , 0 , 0},
                                        { 0 , 0 , 0 , 0 , 1 , 0 , 0},
                                        { 0 , 0 , 0 , 0 , 0 , 1 , 0},
                                        { 0 , 0 , 0 , 0 , 0 , 0 , 1}
                                    };

        return Filter.ImageConvolution (image, matr);
    }

    public static CGBitmapContext ConvertToBitmapRGBA8 (CGImage imageRef)
    {
        // Create an empty bitmap context to draw the uiimage into
        CGBitmapContext context = NewEmptyBitmapRGBA8ContextFromImage (imageRef);
        if (context == null) {
            Console.WriteLine ("ERROR: failed to create bitmap context");
            return null;

        }
        RectangleF rect = new RectangleF (0.0f, 0.0f, imageRef.Width, imageRef.Height);
        context.ClearRect (rect); //Clear memory area from old garbage
        context.DrawImage (rect, imageRef); // Draw image into the context to get the raw image data in our format
        return context; 
    }

    public static CGBitmapContext NewEmptyBitmapRGBA8ContextFromImage (CGImage image)
    {
        CGBitmapContext context = null;
        CGColorSpace colorSpace;
        IntPtr bitmapData;

        int bitsPerComponent = 8;  //Forcing only 8 bit formats for now...

        int width = image.Width;
        int height = image.Height;

        int bytesPerRow = image.BytesPerRow;
        int bufferLength = bytesPerRow * height;

        colorSpace = CGColorSpace.CreateDeviceRGB ();

        if (colorSpace == null) {
            Console.WriteLine ("Error allocating color space RGB");
            return null;
        }

        // Allocate memory for image data
        bitmapData = Marshal.AllocHGlobal (bufferLength);

        //Create bitmap context forcing Premultiplied Alpha as required by Apple iOS
        if (image.AlphaInfo == CGImageAlphaInfo.PremultipliedFirst || image.AlphaInfo == CGImageAlphaInfo.First) {

            context = new CGBitmapContext (bitmapData, 
                                width, 
                                height, 
                                bitsPerComponent, 
                                bytesPerRow, 
                                colorSpace, 
                                CGImageAlphaInfo.PremultipliedFirst);   // ARGB
        } else {

            if (image.AlphaInfo == CGImageAlphaInfo.PremultipliedLast || image.AlphaInfo == CGImageAlphaInfo.Last) {

                context = new CGBitmapContext (bitmapData, 
                                width, 
                                height, 
                                bitsPerComponent, 
                                bytesPerRow, 
                                colorSpace, 
                                CGImageAlphaInfo.PremultipliedLast); //RGBA
            } else {
                Console.WriteLine ("ERROR image format non supported: " + image.AlphaInfo);
                throw new Exception ("ERROR image format non supported: " + image.AlphaInfo);
            }


        }

        if (context == null) {

            Console.WriteLine ("Bitmap context from BitmapData not created");
        }
        return context; 
    }

    public static CGImage ImageConvolution (CGImage image, ConvMatrix fmat)
    {


        //Avoid division by 0
        if (fmat.Factor == 0)
            return image;

        //Create a clone of the original image
        CGImage srcImage = image.Clone ();

        //init some temporary vars
        int x, y, filterx, filtery, tempx, tempy;
        int s = fmat.Size / 2;
        int a, r, g, b, tr, tg, tb, ta;
        int a_div;
        float a_mul;

        //Compute pixel size (bytes per pixel)
        int pixelSize = image.BitsPerPixel / image.BitsPerComponent;

        //Create bitmap contexts
        CGBitmapContext imageData = ConvertToBitmapRGBA8 (image);
        CGBitmapContext srcImageData = ConvertToBitmapRGBA8 (srcImage);

        // Scan0 is the memory address where pixel-array begins.
        IntPtr scan0 = srcImageData.Data;
        // Stride is the width of each row of pixels.
        int stride = srcImageData.BytesPerRow;


        unsafe {
            byte* tempPixel;
            for (y = s; y < srcImageData.Height - s; y++) {
                for (x = s; x < srcImageData.Width - s; x++) {
                    a = r = g = b = 0;
                    a_div = 0;
                    a_mul = 0.0f;

                    //Convolution
                    for (filtery = 0; filtery < fmat.Size; filtery++) {
                        for (filterx = 0; filterx < fmat.Size; filterx++) {

                            // Get nearby pixel's position
                            tempx = x + filterx - s;
                            tempy = y + filtery - s;

                            // Go to that pixel in pixel-array
                            tempPixel = (byte*)scan0 + (tempy * stride) + (tempx * pixelSize);

                            if (srcImageData.AlphaInfo == CGImageAlphaInfo.First) {
                                // The format is ARGB (1 byte each). 
                                ta = (int)*tempPixel;
                                tr = (int)*(tempPixel + 1);
                                tg = (int)*(tempPixel + 2);
                                tb = (int)*(tempPixel + 3);

                                a += fmat.Matrix [filtery, filterx] * ta;
                                r += fmat.Matrix [filtery, filterx] * (tr);
                                g += fmat.Matrix [filtery, filterx] * (tg);
                                b += fmat.Matrix [filtery, filterx] * (tb);
                            }


                            if (srcImageData.AlphaInfo == CGImageAlphaInfo.Last) {
                                // The format is RGBA (1 byte each). 
                                tr = (int)*tempPixel;
                                tg = (int)*(tempPixel + 1);
                                tb = (int)*(tempPixel + 2);
                                ta = (int)*(tempPixel + 3);

                                a += fmat.Matrix [filtery, filterx] * ta;
                                r += fmat.Matrix [filtery, filterx] * (tr);
                                g += fmat.Matrix [filtery, filterx] * (tg);
                                b += fmat.Matrix [filtery, filterx] * (tb);
                            }

                            if (srcImageData.AlphaInfo == CGImageAlphaInfo.PremultipliedFirst) {
                                // The format is premultiplied ARGB (1 byte each). 
                                ta = (int)*tempPixel;
                                tr = (int)*(tempPixel + 1);
                                tg = (int)*(tempPixel + 2);
                                tb = (int)*(tempPixel + 3);

                                // Computing alpha
                                a += fmat.Matrix [filtery, filterx] * ta;
                                a_div = (ta / 255);

                                // Computing rgb
                                if (a_div == 0) {
                                    r += fmat.Matrix [filtery, filterx] * (tr);
                                    g += fmat.Matrix [filtery, filterx] * (tg);
                                    b += fmat.Matrix [filtery, filterx] * (tb);
                                } else {
                                    r += fmat.Matrix [filtery, filterx] * (tr / a_div); // "Dividing the premultiplied value by the   
                                    g += fmat.Matrix [filtery, filterx] * (tg / a_div); //  alpha value to get the original color
                                    b += fmat.Matrix [filtery, filterx] * (tb / a_div); //  value before matrix multiplication"
                                }

                            }


                            if (srcImageData.AlphaInfo == CGImageAlphaInfo.PremultipliedLast) {
                                // The format is premultiplied RGBA (1 byte each). Get em
                                tr = (int)*tempPixel;
                                tg = (int)*(tempPixel + 1);
                                tb = (int)*(tempPixel + 2);
                                ta = (int)*(tempPixel + 3);

                                // Computing alpha
                                a += fmat.Matrix [filtery, filterx] * ta;
                                a_div = (ta / 255);

                                // Computing rgb
                                if (a_div == 0) {
                                    r += fmat.Matrix [filtery, filterx] * (tr);
                                    g += fmat.Matrix [filtery, filterx] * (tg);
                                    b += fmat.Matrix [filtery, filterx] * (tb);
                                } else {

                                    r += fmat.Matrix [filtery, filterx] * (tr / a_div); // "Dividing the premultiplied value by the 
                                    g += fmat.Matrix [filtery, filterx] * (tg / a_div); //  alpha value to get the original color
                                    b += fmat.Matrix [filtery, filterx] * (tb / a_div); //  value before matrix multiplication"
                                }


                            }

                        }
                    }

                    // Remove values out of [0,255]
                    a = Math.Min (Math.Max ((a / fmat.Factor) + fmat.Offset, 0), 255);
                    r = Math.Min (Math.Max ((r / fmat.Factor) + fmat.Offset, 0), 255);
                    g = Math.Min (Math.Max ((g / fmat.Factor) + fmat.Offset, 0), 255);
                    b = Math.Min (Math.Max ((b / fmat.Factor) + fmat.Offset, 0), 255);

                    // Premultiplying color value by alpha value if needed by image format
                    if (srcImageData.AlphaInfo == CGImageAlphaInfo.PremultipliedFirst || srcImageData.AlphaInfo == CGImageAlphaInfo.PremultipliedLast) {
                        a_mul = (a / 255.0f);
                        r = (int)(r * a_mul);  
                        g = (int)(g * a_mul);    
                        b = (int)(b * a_mul);
                    }

                    // Finally compute new pixel position (in new image) and write the pixels.
                    if (srcImageData.AlphaInfo == CGImageAlphaInfo.PremultipliedFirst || srcImageData.AlphaInfo == CGImageAlphaInfo.First) {
                        // The format is ARGB (1 byte each) 
                        byte* newpixel = (byte*)imageData.Data + (y * imageData.BytesPerRow) + (x * pixelSize);
                        *newpixel = (byte)a;
                        *(newpixel + 1) = (byte)r;
                        *(newpixel + 2) = (byte)g;
                        *(newpixel + 3) = (byte)b;
                    }


                    if (srcImageData.AlphaInfo == CGImageAlphaInfo.PremultipliedLast || srcImageData.AlphaInfo == CGImageAlphaInfo.Last) {
                        // The format is RGBA (1 byte each)
                        byte* newpixel = (byte*)imageData.Data + (y * imageData.BytesPerRow) + (x * pixelSize);
                        *newpixel = (byte)r;
                        *(newpixel + 1) = (byte)g;
                        *(newpixel + 2) = (byte)b;
                        *(newpixel + 3) = (byte)a;
                    }
                }
            }
        }

        return imageData.ToImage ();
    }

}
}

1 个答案:

答案 0 :(得分:0)

嗯,有很多方法可以改进代码,比如将所有决策移出主循环;在外面执行它们,使用它们为该循环调用提供方法。

然而,主要的是要找出你是否可以包装Core Image,它应该非常快,因为它将使用GPU上的着色器来完成它。