如何在MonoTouch中优化此图像卷积滤镜方法? [英] How to Optimize this Image convolution filter Method in MonoTouch?

查看:64
本文介绍了如何在MonoTouch中优化此图像卷积滤镜方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

意识到不存在MonoTouch的实时图形效果库后,我决定编写自己的。经过研究后,我编写了一种卷积方法,该方法可以很好地工作,但是即使使用不安全的代码,也非常慢。
我做错了什么?我缺少一些优化吗?

After having realized that no realtime graphics effect library exists for MonoTouch, I decided to write my own. After some research I've written a convolution method that works perfectly but, even using unsafe code, is VERY SLOW. What I'm doing wrong? Is there some optimization that I'm missing?

这是我的c#类,欢迎大家提出建议,无论大小如何!

Here is my c# class, any suggestion, not matter how small, is welcome!

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 ();
    }

}
}


推荐答案

好吧,可以做很多事情来改进代码,例如将所有这些决策移出主循环。

Well, there's lots that can be done to improve that code, like moving all those decisions out of the main loop; do them outside, use them to provide a method for that loop to call.

但是,最主要的是要弄清楚是否可以包装Core Image,这应该确实非常快,因为它将使用GPU上的着色器来完成。

However, the main thing would be to find out if you can wrap Core Image, which ought to be very fast indeed, as it will do it using shaders on the GPU.

这篇关于如何在MonoTouch中优化此图像卷积滤镜方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆