文档章节

C#调用GDI+1.1中的函数实现高斯模糊、USM锐化等经典效果。

abcijkxyz
 abcijkxyz
发布于 2016/11/22 16:40
字数 1738
阅读 10
收藏 0
点赞 0
评论 0

        在GDI+1.1的版本中,MS加入不少新的特性,其中的特效类Effect就是一个很有吸引力的东西,可惜在VS2010的Image类中,却没有把这个类封装进来(不晓得是不是我没有发现),这个也许MS也有自己的考虑的,毕竟要使用这些函数,必须要求系统是Windows Vista及其以上,而XP的市场占有率在那个时候还比较高的。  
       不过,作为一种选择,我们有义务把这些函数给哪些已经按照了这些最新系统的客户使用。
     
       其实,这些函数我在VB6下两年前就已经调用过,调用的方式也很简单明了,现在,在学习C#,就要考虑如何将他们封装入C#中。虽然哪些算法的更底层(像素级别的处理实现)实现在很早之前就已经实现,但是能够直接调用现有的函数对于不少朋友来说还是一件很幸福的事情的。

        实现这个功能的第一步就是要找到这些函数的声明,这个在MSDN上有C风格的声明,改成C#语言的大部分都不成问题,参考 http://msdn.microsoft.com/en-us/library/ms533971(VS.85).aspx

   例如,这个 

  GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect *effect, RECT *roi, BOOL useAuxData, VOID **auxData, INT *auxDataSize)


        我写成这样:

[DllImport( "gdiplus.dll",SetLastError = true, ExactSpelling = true,CharSet = CharSet.Unicode)]
        private static extern int GdipBitmapApplyEffect(IntPtr bitmap, IntPtr effect, ref Rectangle rectOfInterest, bool useAuxData, IntPtr auxData, int auxDataSize);


        对于第一个参数bitmap,你无法声明为C#的Bitmap类的,或者你也可以声明为HandleRef类型的,VS就是这么干的, 对于最后几个参数,是用来给用户返回一些数据,基本上不会有人对那几个数据感兴趣,因此你声不声明为out类型的参数也无所谓。

        问题来了,第一个参数bitmap的本意是GDI+的image对象的句柄,在C#中,有Bitmap类,实际上我们知道他就是GDI+的封装,那么他的具体的实例中肯定也对应了一个GDI+对象的句柄,但是他封装的太厉害了,未给我们提供这个借口,这样一来,我们有两种选择,一是直接调用GDI+的加载图像的函数,得到对应的句柄,然后处理,然后调用GDI+的绘图API显示,但是这样无疑会增加工程量;二是我们强力爆破,寻找C#封装预留的后门,看能不能偷偷摸摸的得到这个句柄。呵呵,本人初学C#,还没这个火候,不过从高人哪些偷到一个代码,却是可以:

/// <summary>
        /// 获取对象的私有字段的值,感谢Aaron Lee Murgatroyd
        /// </summary>
        /// <typeparam name="TResult">字段的类型</typeparam>
        /// <param name="obj">要从其中获取字段值的对象</param>
        /// <param name="fieldName">字段的名称.</param>
        /// <returns>字段的值</returns>
        /// <exception cref="System.InvalidOperationException">无法找到该字段.</exception>
        /// 
        internal static TResult GetPrivateField<TResult>(this object obj, string fieldName)
        {
            if (obj == null) return default(TResult);
            Type ltType = obj.GetType();
            FieldInfo lfiFieldInfo = ltType.GetField( fieldName,System.Reflection.BindingFlags.GetField |System.Reflection.BindingFlags.Instance |System.Reflection.BindingFlags.NonPublic);
            if (lfiFieldInfo != null)
                return (TResult)lfiFieldInfo.GetValue(obj);
            else
                throw new InvalidOperationException(string.Format("Instance field '{0}' could not be located in object of type '{1}'.",fieldName, obj.GetType().FullName));
        }


       通过这个代码,如果你知道被封装的私有字段的名称,就可以获得该字段的值(原理我还看不懂)。
        好了,那我们如何知道C#封装的那个GDI+句柄的值呢,有办法,相信每个C#高手身边都会有个类似Refleator这样的工具吧,直接去看看Image类的实现吧。

       以下是从代码中贴过来的:

public static IntPtr NativeHandle(this Bitmap Bmp)
        {
            return Bmp.GetPrivateField<IntPtr>("nativeImage");
            /*  用Reflector反编译System.Drawing.Dll可以看到Image类有如下的私有字段
                internal IntPtr nativeImage;
                private byte[] rawData;
                private object userData;
                然后还有一个 SetNativeImage函数
                internal void SetNativeImage(IntPtr handle)
                {
                    if (handle == IntPtr.Zero)
                    {
                        throw new ArgumentException(SR.GetString("NativeHandle0"), "handle");
                    }
                    this.nativeImage = handle;
                }
                这里在看看FromFile等等函数,其实也就是调用一些例如GdipLoadImageFromFile之类的GDIP函数,并把返回的GDIP图像句柄
                通过调用SetNativeImage赋值给变量nativeImage,因此如果我们能获得该值,就可以调用VS2010暂时还没有封装的GDIP函数
                进行相关处理了,并且由于.NET肯定已经初始化过了GDI+,我们也就无需在调用GdipStartup初始化他了。
             */
        }


       OK。万事大吉了,
       下面就是函数的调用了,比如高斯模糊的效果,就是几个函数的调用,多么简单啊。

/// <summary>
        /// 对图像进行高斯模糊,参考:http://msdn.microsoft.com/en-us/library/ms534057(v=vs.85).aspx
        /// </summary>
        /// <param name="Rect">需要模糊的区域,会对该值进行边界的修正并返回.</param>
        /// <param name="Radius">指定高斯卷积核的半径,有效范围[0,255],半径越大,图像变得越模糊.</param>
        /// <param name="ExpandEdge">指定是否对边界进行扩展,设置为True,在边缘处可获得较为柔和的效果. </param>
            
        public static void GaussianBlur(this Bitmap Bmp, ref Rectangle Rect, float Radius = 10, bool ExpandEdge = false)
        {
            int Result;
            IntPtr BlurEffect;
            BlurParameters BlurPara;
            if ((Radius <0) || (Radius>255)) 
            {
                throw new ArgumentOutOfRangeException("半径必须在[0,255]范围内");
            }
            BlurPara.Radius = Radius ;
            BlurPara.ExpandEdges = ExpandEdge;
            Result = GdipCreateEffect(BlurEffectGuid, out BlurEffect);
            if (Result == 0)
            {
                IntPtr Handle = Marshal.AllocHGlobal(Marshal.SizeOf(BlurPara));
                Marshal.StructureToPtr(BlurPara, Handle, true);
                GdipSetEffectParameters(BlurEffect, Handle, (uint)Marshal.SizeOf(BlurPara));
                GdipBitmapApplyEffect(Bmp.NativeHandle(), BlurEffect, ref Rect, false, IntPtr.Zero, 0);
                // 使用GdipBitmapCreateApplyEffect函数可以不改变原始的图像,而把模糊的结果写入到一个新的图像中
                GdipDeleteEffect(BlurEffect);
                Marshal.FreeHGlobal(Handle);
            }
            else
            {
                throw new ExternalException("不支持的GDI+版本,必须为GDI+1.1及以上版本,且操作系统要求为Win Vista及之后版本.");
            }
        }


  注意函数的第一个参数 this Bitmap Bmp,有了这个this,在你声明一个Bitmap类型变量后的只能提示里是不是有了这一项:



       什么原理,我还没有学到哪一步,呵呵。

      在实例代码中,我只提供了高斯模糊和USM锐化效果,其他的特效(色彩平衡、亮度对比度、红眼消除、色相饱和度、色阶、曲线等)大家查查MSDN模仿着也就写出来了,其实这里最重要的我认为还是高斯模糊,因为他是众多算法的基础,比如USM锐化就是基于高斯模糊的,所以他比高斯模糊的速度慢,还有比如高反差保留,Canny边缘算子,选区的羽化等等。

       最后说一点图像滤镜的调整时的预览效果,预览时肯定要保留一份原始数据的,这个我还是倾向于直接用内存处理,最好不要经过类的封装的模式,大家看看代码可能就知道我说对的是什么意思了。

一个简单的UI效果:



      代码下载地址:
  http://files.cnblogs.com/Imageshop/GdipEffect.rar

       注意GDIP模糊的一个特性,模糊半径越大,所用的时间久越少,所以算法的优化是很重要的。

本文转载自:http://www.cnblogs.com/Imageshop/archive/2012/12/13/2815712.html

共有 人打赏支持
abcijkxyz
粉丝 60
博文 6196
码字总数 1876
作品 0
深圳
项目经理
实时图像处理和机器学习库 - cv4j

The target is to set up a high quality and real-time image process and machine learning library which is implemented in pure java. The framework can run application on java desk......

匿名
2017/06/13
394
0
用C#的GDI+技术生成复杂型彩色验证码

该类是生成一个验证码的类。本人集合了网上大部分的C#关于GDI+的文章进行多次改进,现在已经形成了可在生产环节中使用的验证码。 该验证码加入了背景噪点,背景噪点曲线和直线,背景噪点文字...

Minho
2012/12/13
0
2
【数字图像处理】七.MFC图像增强之图像普通平滑、高斯平滑、Laplacian、Sobel、Prewitt锐化详解

本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行讲解,主要通过MFC单文档视图实现显示BMP图像增强处理,包括图像普通平滑、高斯平滑、...

Eastmount
2015/06/08
0
0
计算机视觉-高斯滤波

高斯滤波 高斯滤波: 高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。高斯滤波是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和领域内的其...

googler_offer
2017/11/28
0
0
妹纸们的最爱 - 拓幻科技美颜SDK算法

现在各大手机制造商都在主推美颜效果,各种前后2000万像素,照亮你的美,各种逆光也清晰。其实这些看似神秘的美颜效果,除了依赖于手机像素之外,更重要的是攻城狮们对于美颜算法的构造。除了...

tillusory
01/31
0
0
Qt Quick 图像处理实例之美图秀秀(附源码下载)

在《Qt Quick 之 QML 与 C++ 混合编程详解》一文中我们讲解了 QML 与 C++ 混合编程的方方面面的内容,这次我们通过一个图像处理应用,再来看一下 QML 与 C++ 混合编程的威力,同时也为诸君揭...

foruok
2014/07/16
0
0
Unity Shader入门精要学习笔记 - 第12章 屏幕后处理效果

Unity Shader入门精要学习笔记 - 第12章 屏幕后处理效果 本系列为UnityShader入门精要读书笔记总结, 原作者博客链接:http://blog.csdn.net/candycat1992/article/ 书籍链接:http://produc...

wwlcsdn000
01/02
0
0
【图像处理】二维付立叶变换和滤波 (Two-Dimensional Fourier Transform and Filtering)

实验要求   该实验的目的是开发一个2-D FFT 程序包。要求程序能完成下面的功能:   (1.a) 用因子 (-1)x+y 乘以输入图像,以实现滤波的中心化变换;   (1.b) 计算付立叶变换;   (1.c)...

u013165921
01/15
0
0
自定义高斯模糊"Dialog",使用起来只需要new and show

转载请注明出处:王亟亟的大牛之路 先安利:https://github.com/ddwhan0123/Useful-Open-Source-Android 礼拜1做的一个功能里美工大姐姐需要显示一个点击弹窗+iOS形式的背景虚化的View,公司原...

ddwhan0123
2016/07/30
0
0
图像处理之vImage(二)——卷积

vImage学习笔记——卷积(Convolution) 卷积(Convolution)是一个常用的图像处理技术,可以改变像素强度,从而影响周围其他像素的强度。卷积的常用技术是创建滤镜,使用卷积技术,你可以获...

云xiao菲
2016/08/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

shell中的函数、shell中的数组、告警系统需求分析

shell中的函数 格式: 格式: function f_name() { command } 函数必须要放在最前面 示例1(用来打印参数) 示例2(用于定义加法) 示例3(用于显示IP) shell中的数组 shell中的数组1 定义数...

Zhouliang6
今天
2
0
用 Scikit-Learn 和 Pandas 学习线性回归

      对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了。 1. 获取数据,定义问题     没有...

wangxuwei
今天
1
0
MAC安装MAVEN

一:下载maven压缩包(Zip或tar可选),解压压缩包 二:打开终端输入:vim ~/.bash_profile(如果找不到该文件新建一个:touch ./bash_profile) 三:输入i 四:输入maven环境变量配置 MAVEN_HO...

WALK_MAN
今天
0
0
33.iptables备份与恢复 firewalld的9个zone以及操作 service的操作

10.19 iptables规则备份和恢复 10.20 firewalld的9个zone 10.21 firewalld关于zone的操作 10.22 firewalld关于service的操作 10.19 iptables规则备份和恢复: ~1. 保存和备份iptables规则 ~2...

王鑫linux
今天
2
0
大数据教程(2.11):keeperalived+nginx高可用集群搭建教程

上一章节博主为大家介绍了目前大型互联网项目的系统架构体系,相信大家应该注意到其中很重要的一块知识nginx技术,在本节博主将为大家分享nginx的相关技术以及配置过程。 一、nginx相关概念 ...

em_aaron
今天
1
0
Apache Directory Studio连接Weblogic内置LDAP

OBIEE默认使用Weblogic内置LDAP管理用户及组。 要整理已存在的用户及组,此前办法是导出安全数据,文本编辑器打开认证文件,使用正则表达式获取用户及组的信息。 后来想到直接用Apache Dire...

wffger
今天
2
0
HFS

FS,它是一种上传文件的软件。 专为个人用户所设计的 HTTP 档案系统 - Http File Server,如果您觉得架设 FTP Server 太麻烦,那么这个软件可以提供您更方便的档案传输系统,下载后无须安装,...

garkey
今天
1
0
Java IO类库之BufferedInputStream

一、BufferedInputStream介绍 /** * A <code>BufferedInputStream</code> adds * functionality to another input stream-namely, * the ability to buffer the input and to * sup......

老韭菜
今天
0
0
STM 32 窗口看门狗

http://bbs.elecfans.com/jishu_805708_1_1.html https://blog.csdn.net/a1985831055/article/details/77404131...

whoisliang
昨天
1
0
Dubbo解析(六)-服务调用

当dubbo消费方和提供方都发布和引用完成后,第四步就是消费方调用提供方。 还是以dubbo的DemoService举例 -- 提供方<dubbo:application name="demo-provider"/><dubbo:registry address="z...

青离
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部