分享
 
 
 

基于.NET数字处理程序的框架设计

王朝c#·作者佚名  2008-05-30
窄屏简体版  字體: |||超大  

接触数字图像处理最早是在高中,那时候PHOTOSHOP还是4.0,可能是因为先入为主的关系,到现在都没有学3DMAX之类的兴趣,2D到3D的飞跃估计是没我什么事了,舍不得那平方到立方的高薪....呵呵。

在上大学的时候,就和同学一起写过一些图像处理的程序,那个时候编程还很随意,考虑的只是如何实现,现在看来真正的技术是把握全局的能力,而不是灵光一现的神奇。前些日子接触了一些国外的图像处理程序,在这里算是作个总结,估计以后不会再针对性的研究图像处理方面的东西了。

以前的一个同学曾经跟我说过.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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有