基于.NET数字处理程序的框架设计
点击次数:19 次 发布日期:2008-11-26 12:36:00 作者:源代码网
|
源代码网推荐 在上大学的时候,就和同学一起写过一些图像处理的程序,那个时候编程还很随意,考虑的只是如何实现,现在看来真正的技术是把握全局的能力,而不是灵光一现的神奇。前些日子接触了一些国外的图像处理程序,在这里算是作个总结,估计以后不会再针对性的研究图像处理方面的东西了。 源代码网推荐 以前的一个同学曾经跟我说过.net没有指针,现在很多培训课好像也是这么讲的,其实这是一个谬误。只是framework不推荐使用指针,尤其是在webservise,remoting等跨进程操作中,指针都是不安全的。但用过TC的各位都应该对指针的执行效率又深刻的印象,在批量运算大规模数据的需求下,指针是不二的选择。因而.net聪明的保留的保留了指针,并将其列入不安全方法集中。合理的使用指针将大幅度提高执行效率,我曾做过试验,对640*480的图像进行逐点运算,非指针运算要执行数分钟,而指针运算几乎是瞬间完成的。所以不要害怕使用指针。 源代码网推荐 其次就是数学,奉劝大家一定要弄明白了再写程序,数学课不是闹着玩的......想不明白就要躺在床上反复的想,我总觉得数学能预防老年痴呆。 源代码网推荐 言归正传,说说程序结构吧 : 源代码网推荐 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; 源代码网推荐 } 源代码网推荐 这就是。。。我数学不好的下场。都不知道她在说什么呢,最小值中选出最大值。算法不难找,关键是要看结构如何将他们整合起来。 源代码网推荐 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; 源代码网推荐 }就是这个,有些事情,大师知道就够了,你就照着去做就行了,为什么?因为你可能一辈子也不明白,自然有人会去弄明白的,知识还在传承。就像你不必知道自己的胃酸比例,也可以放心的吃香喝辣一样,也不必担心子孙后代消化不良。有些事情不必强求,有点消极了,呵呵。 源代码网推荐 画面并不难,只要把握好调用关系就可以了,另外像photoshop那样的悬浮窗体是最佳的选择我认为, // 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); 源代码网推荐 }欢迎大家跟我讨论 源代码网推荐 源代码网推荐 http://www.cnblogs.com/phono/archive/2006/09/07/phono.html 源代码网推荐 源代码网推荐 源代码网推荐 做人要厚道,请注明转自酷网动力(www.ASPCOOL.COM)。 源代码网推荐 源代码网供稿. |
