数字图像处理(转)
                                                               Imaging项目(滤镜,纹理,图像模式)
                                                                Math项目(算法,边界,定制。及常用计算方法)
                                                                主程序项目
        各举个例子来说明,我也来一回面向接口编程 ,

public interface IFilter
{
Bitmap Apply( Bitmap img );
}

举例来说明,我也来一回面向接口编程 ,各滤镜都要实现这个接口,接口定义还包括一个不生成实际图像,只生成二进制对象的借口定义,在这里暂不作考虑。以取反色滤镜为例
public Bitmap Apply( Bitmap srcImg )
        {
            // get source image size
            int width = srcImg.Width;
            int height = srcImg.Height;
           
            PixelFormat fmt = ( srcImg.PixelFormat == PixelFormat.Format8bppIndexed ) ?
                        PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb;

            // lock source bitmap data
            BitmapData srcData = srcImg.LockBits(
                new Rectangle( 0, 0, width, height ),
                ImageLockMode.ReadOnly, fmt );

            // create new image
            Bitmap dstImg = ( fmt == PixelFormat.Format8bppIndexed ) ?
                        AForge.Imaging.Image.CreateGrayscaleImage( width, height ) :
                        new Bitmap( width, height, fmt );

            // lock destination bitmap data
            BitmapData dstData = dstImg.LockBits(
                new Rectangle( 0, 0, width, height ),
                ImageLockMode.ReadWrite, fmt );

            // copy image
            Win32.memcpy( dstData.Scan0, srcData.Scan0, srcData.Stride * height );

            // process the filter
            ProcessFilter( dstData, fmt );

            // unlock both images
            dstImg.UnlockBits( dstData );
            srcImg.UnlockBits( srcData );

            return dstImg;
        }


是该滤镜方法的入口,完成了处理前的准备工作,ProcessFilter同时调用每个滤镜类中共有的ProcessFilter方法,而这个ProcessFilter就是实现功能的关键所在了逐点运算或模版运算。
// Process the filter
        private unsafe void ProcessFilter( BitmapData data, PixelFormat fmt )
        {
            int width    = data.Width;
            int height    = data.Height;

            int lineSize = width * ( ( fmt == PixelFormat.Format8bppIndexed ) ? 1 : 3 );
            int offset = data.Stride - lineSize;

            // do the job
            byte * ptr = (byte *) data.Scan0.ToPointer( );

            // invert
            for ( int y = 0; y < height; y++ )
            {
                for ( int x = 0; x < lineSize; x++, ptr ++ )
                {
                    // ivert each pixel
                    *ptr = (byte)( 255 - *ptr );
                }
                ptr += offset;
            }
        }

其中Format8bppIndexed是不必太关心的,个人认为设计初期可以不用考虑兼容它的问题。
下面来说说纹理,这个以前考虑得还不太多,但发现老外很喜欢玩这个,因为纹理在数学方面发挥的空间更大,我也不知道他们是怎么想出来的,凭空想可能还真是有难度,可能是他们谁在玩数学建模软件的时候发现这个玩法的,于是高数老师谁也不服谁,把算法玩的火火的。反正我觉得是这么回事。。。
    public interface ITextureGenerator
    {
        /**//// <summary>
        /// Generate texture
        /// </summary>
        float[,] Generate( int width, int height );

        /**//// <summary>
        /// Reset - regenerate internal random numbers
        /// </summary>
        void Reset( );
    }
这是纹理生成器的实现接口,为了保证每次的纹理不同,还要更新随机数以作为计算参数
        private Math.PerlinNoise noise = new Math.PerlinNoise( 1.0 / 32, 0.05, 0.5, 8 );
实现纹理细节还需要靠noise实现,因而需要实现许多种noise。
        // Constructors
        public WoodTexture( ) : this( 12.0 ) { }
        public WoodTexture( double rings )
        {
            this.rings = rings;
            Reset( );
        }

构造函数提供了默认值的设置,也就是对单位纹理大小的限定。
        // Generate texture
        public float[,] Generate( int width, int height )
        {
            float[,]    texture = new float[height, width];
            int            w2 = width / 2;
            int            h2 = height / 2;

            for ( int y = 0; y < height; y++ )
            {
                for ( int x = 0; x < width; x++ )
                {
                    double xv = (double) ( x - w2 ) / width;
                    double yv = (double) ( y - h2 ) / height;

                    texture[y, x] =
                        Math.Max( 0.0f, Math.Min( 1.0f, (float)
                        Math.Abs( Math.Sin(
                            ( Math.Sqrt( xv * xv + yv * yv ) + noise.Function2D( x + r, y + r ) )
                                * Math.PI * 2 * rings
                        ))
                        ));
                }
            }
            return texture;
        }
这就是。。。我数学不好的下场。都不知道她在说什么呢,最小值中选出{zd0}值。算法不难找,关键是要看结构如何将他们整合起来。
        public void Reset( )
        {
            r = rand.Next( 5000 );
        }别忘了这个随机数,数字的图像也需要自然的美。

Math工程中面向对象的观念不它容易得到贯彻,看一看那个PerlinNoise吧,抛砖引玉。
        public PerlinNoise( double initFrequency, double initAmplitude, double persistance, int octaves )
        {
            this.initFrequency    = initFrequency;
            this.initAmplitude    = initAmplitude;
            this.persistance    = persistance;
            this.octaves        = octaves;
        }
首先要收集数据,因为图像处理要涉及到一维和二维两种情况,因而像noise这种底层方法要分别对应着两种情况给出对应的方法。
        /**//// <summary>
        /// 1-D Perlin noise function
        /// </summary>
        public double Function( double x )
        {
            double    frequency = initFrequency;
            double    amplitude = initAmplitude;
            double    sum = 0;
           
            // octaves
            for ( int i = 0; i < octaves; i++ )
            {
                sum += SmoothedNoise( x * frequency ) * amplitude;

                frequency *= 2;
                amplitude *= persistance;
            }
            return sum;
        }

        /**//// <summary>
        /// 2-D Perlin noise function
        /// </summary>
        public double Function2D( double x, double y )
        {
            double    frequency = initFrequency;
            double    amplitude = initAmplitude;
            double    sum = 0;
           
            // octaves
            for ( int i = 0; i < octaves; i++ )
            {
                sum += SmoothedNoise( x * frequency, y * frequency ) * amplitude;

                frequency *= 2;
                amplitude *= persistance;
            }
            return sum;
        }
一维跟二维的区别是什么,上中学的时候知道了线的运动生成了面,上大学又知道了循环着变化着的线能代表面,但如果做过了边缘识别和锐化以后话,又发现以前小看线了,其实它只是比面少一个参数而已。


        /**//// <summary>
        /// Ordinary noise function
        /// </summary>
        protected double Noise( int x )
        {
            int n = ( x << 13 ) ^ x;

            return ( 1.0 - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) & 0x7fffffff ) / 1073741824.0 );
        }
        protected double Noise( int x, int y )
        {
            int n = x + y * 57;
            n = ( n << 13 ) ^ n ;

            return ( 1.0 - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) & 0x7fffffff ) / 1073741824.0 );
        }又一次证明了前面那段话,个人感觉这个x+y*57有点投影的意思。获取相应的噪点值。但噪点不是直接就能拿来用的
        /**//// <summary>
        /// Smoothed noise
        /// </summary>
        protected double SmoothedNoise( double x )
        {
            int        xInt = (int) x;
            double    xFrac = x - xInt;

            return CosineInterpolate( Noise( xInt ) , Noise( xInt + 1 ), xFrac );
        }
        protected double SmoothedNoise( double x, double y )
        {
            int        xInt = (int) x;
            int        yInt = (int) y;
            double    xFrac = x - xInt;
            double    yFrac = y - yInt;

            // get four noise values
            double    x0y0 = Noise( xInt    , yInt );
            double    x1y0 = Noise( xInt + 1, yInt );
            double    x0y1 = Noise( xInt    , yInt + 1 );
            double    x1y1 = Noise( xInt + 1, yInt + 1) ;

            // x interpolation
            double    v1 = CosineInterpolate( x0y0, x1y0, xFrac );
            double    v2 = CosineInterpolate( x0y1, x1y1, xFrac );
            // y interpolation
            return CosineInterpolate( v1, v2, yFrac );
        }平滑的噪点,这个称呼似乎有点不协调,通过余弦插值,而不是离散余弦来运算。什么是余弦插值呢?         /**//// <summary>
        /// Cosine interpolation
        /// </summary>
        protected double CosineInterpolate( double x1, double x2, double a )
        {
            double f = ( 1 - Math.Cos( a * Math.PI ) ) * 0.5;

            return x1 * ( 1 - f ) + x2 * f;
        }就是这个,有些事情,大师知道就够了,你就照着去做就行了,为什么?因为你可能一辈子也不明白,自然有人会去弄明白的,知识还在传承。就像你不必知道自己的胃酸比例,也可以放心的吃香喝辣一样,也不必担心子孙后代xxxx。有些事情不必强求,有点消极了,呵呵。
画面并不难,只要把握好调用关系就可以了,另外像photoshop那样的悬浮窗体是{zj0}的选择我认为,         // Invert image
        private void invertColorFiltersItem_Click(object sender, System.EventArgs e)
        {
            ApplyFilter(new Invert());
        }

        // Apply filter on the image
        private void ApplyFilter(IFilter filter)
        {
            try
            {
                // set wait cursor
                this.Cursor = Cursors.WaitCursor;

                // apply filter to the image
                Bitmap newImage = filter.Apply(image);

                if (host.CreateNewDocumentOnChange)
                {
                    // open new image in new document
                    host.NewDocument(newImage);
                }
                else
                {
                    if (host.RememberOnChange)
                    {
                        // backup current image
                        if (backup != null)
                            backup.Dispose();

                        backup = image;
                    }
                    else
                    {
                        // release current image
                        image.Dispose();
                    }

                    image = newImage;

                    // update
                    UpdateNewImage();
                }
            }
            catch (ArgumentException)
            {
                MessageBox.Show("Selected filter can not be applied to the image", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                // restore cursor
                this.Cursor = Cursors.Default;
            }
        }调用顺畅的话,多少代码都不会觉得乱,对于初学者来说,要善用region。
这里还有个DocumentsHost的概念,用它来承载图像文件,并将图像和窗体连接起来,很方便     /**//// <summary>
    /// IDocumentsHost interface
    /// Provides connectione between documents and the main widnow
    /// </summary>
    public interface IDocumentsHost
    {
        bool CreateNewDocumentOnChange{get;}
        bool RememberOnChange{get;}

        bool NewDocument(Bitmap image);
        bool NewDocument(ComplexImage image);

        Bitmap GetImage(object sender, String text, Size size, PixelFormat format);
    }



郑重声明:资讯 【数字图像处理(转)】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——