热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数字图像点运算实践(直方图均衡和分段线性拉伸)

zsmhttp:i.cnblogs.comEditPosts.aspx?opt1摘要人类所获得的信息大约70%来自于图像,数字图像处理是计算机对采样量化

zsm  http://i.cnblogs.com/EditPosts.aspx?opt=1

摘要

人类所获得的信息大约70%来自于图像,数字图像处理是计算机对采样量化的图像进行去除噪声、增强、复原、分割、提前特征等处理的方法和技术,它对一个物体的数字表示施加一系列的操作,以得到所期望的结果。这种技术在航空航天、生物医学、通信工程、军事、文化、电子商务等方面应用广泛。本实验根据课堂所学的关于数字图像点运算的知识实现对数字图像的灰度直方图均衡处理分段线性拉伸处理

 

关键词

数字图像  点运算  灰度直方图  直方图均衡  分段线性拉伸

一、 任务说明

用Java编程语言实现一个带GUI的数字图像处理程序,实现读出数字图像并进行灰度直方图均衡处理和分段线性拉伸处理的功能。

需要注意的是,在本程序中,对于分段线性拉伸处理,程序中会先根据源图像的灰度直方图找出集中分布区间(像素个数之和占图像总像素个数85%以上的区间中长度最短的区间),然后让用户输入一个要拉伸到的区间,再进行分段线性拉伸。拉伸前后灰度范围都默认为 [0,255]。

二、 算法原理

(一)直方图均衡

1、            背景意义

直方图反映的是一幅图像的灰度级与出现这种灰度级概率之间关系的图形。直方图均衡的目的是使所有灰度级出现的相对概率相同,此时图像的熵最大,图像包含的信息最大。经过直方图均衡处理后,图像的对比度会增强,特别是对于背景或前景都太亮或太暗的图像非常有用。例如,其可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。

直方图均衡的主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景噪声的对比度并且降低有用信号的对比度。

 

2、            基本算法

若灰度级概率分布函数为 ,则此时直方图均衡的变换函数 s=T[r] 应该满足如下条件:

 

(a)       T[r]为单值单调递增函数

(b)       0

由转换原理可求得

则原灰度值 r 经变换后对应的新灰度值为

 

3、            扩展算法

上述基本算法是对于灰度级连续的情况而言的,而在计算机中图像的灰度级是离散的,实际上与连续情况类似,我们可以得到离散情况下的变换函数:

假定离散情况下共有L个灰度级,第k个灰度级出现的像素个数为,图像的总像素个数为N,则第k个灰度级出现的概率为:

 

从而均衡化的变换函数为:

 

则原灰度值r对应的新灰度值为

因此通过转换原图像每个像素的灰度值就可实现直方图均衡处理。

 

(二)分段线性拉伸

1、            背景意义

一般成像系统只具有一定的亮度响应范围,亮度的最大值与最小值之比称为对比度。由于成像系统的限制,常常出现对比度不足的问题,使人眼观看图像时的视觉效果较差,可以采用灰度变换(灰度修正)方法提高图像的对比度,增强图像。常用的灰度修正方法有:

(1)       线性变换

(2)       分段线性变换

(3)       非线性变换,如指数变换、对数变换等

这里采用的是 分段线性变换

2、            基本算法

为了突出感兴趣的目标或灰度区间,相对抑制不感兴趣的灰度区间,可以采用分段线性变换。常用三段线性变换方法。

设原灰度直方图的灰度级的分布范围为[0,],集中分布在[a,b],又设变换后的直方图灰度级的分布范围为[0,],集中分布在[c,d],则变换公式如下:

对 :

对 :

对 :

3、            扩展算法

在本实验中,和都当成255,而原灰度级的集中分布范围 [a,b] 是通过程序算出来的,即从原图像的灰度直方图中找出一个连续灰度区间 [a,b] ,这个区间满足两个条件:

 

(1)       在此灰度级区间内的像素个数和占总像素个数的85%以上

(2)       此区间是满足(1)的区间中区间长度最短的

 

注:上述比例85%是在程序中默认的,其实应该做成能让用户自己输入一个比例,然后程序找出这个比例下灰度级的集中分布区间,但由于时间有限,程序中只默认为85%,没有实现让用户输入比例的功能。

三、 算法实现

功能函数:功能说明,输入参数说明,输出参数说明,算法流程(代码或伪代码,注释)

(一)         使用语言

本程序采用Java语言编写,开发平台为Eclipse (Version: Juno)

(二)         编译环境

java version "1.7.0_25"

Java(TM) SE Runtime Environment (build 1.7.0_25-b17)

(三)         功能函数

1、private BufferedImage getGrayPicture(BufferedImage originalImage)

(1)   功能说明:此函数获得输入图像的灰度图像,以及对应的灰度分布

(2)   输入参数说明:originalImage为输入的图像,类型为BufferedImage

(3)   输出参数说明:调用此函数后得到输入图像对应的灰度图像,并把个灰度级的像素个数存入全局数组huidunum[]中,供后面处理之用

(4)   算法流程:

遍历每个像素——>

获得该像素RGB值——>

获得R、G、B分量——>

得到灰度值——>

以灰度值为新R、G、B分量,得到新RGB值——>

新像素

 

    // 获得图像的灰度图,并得到该图像个灰度级的像素个数 huidunum[]

    private BufferedImage getGrayPicture(BufferedImage originalImage) {

        int rgb, gray, alph, red, green, blue;

        int imgwidth = originalImage.getWidth();

        int imgheight = originalImage.getHeight();

        BufferedImage huiduimg = new BufferedImage(imgwidth, imgheight,

                originalImage.getType());// 不在原图像上修改,而是创建同样大小的一张图片

        for (int i &#61; 0; i <256; i&#43;&#43;)

            huidunum[i] &#61; 0; // 灰度分布初始化为0&#xff0c;即各灰度值像素个数初始化为0

        for (int i &#61; 0; i

            for (int j &#61; 0; j

                rgb &#61; originalImage.getRGB(i, j);// 获得该像素的rgb值&#xff0c;为一个4字节的整数&#xff0c;从高到低各字节值

                                                    // 分别表示

                                                    // 透明度appha、red、green、blue

 

                alph &#61; rgb >> 24; // 得到appha的值

                red &#61; (rgb & 0xff0000) >> 16;// 得到red值

                green &#61; (rgb & 0xff00) >> 8;// 得到green值

                blue &#61; (rgb & 0xff);// 得到blue值

 

                gray &#61; (int) (red * 299 &#43; green * 587 &#43; blue * 114) / 1000;// 根据公式由rgb三分量值得到灰度值gray

 

                huidunum[gray]&#43;&#43;;// 该灰度值像素个数加一

 

                rgb &#61; (alph <<24) | (gray <<16) | (gray <<8) | gray;// 由灰度值转rgb值&#xff0c;三分量都是gray值

                huiduimg.setRGB(i, j, rgb);// 新rgb

            }

        }

        return huiduimg;

}

 

2、private BufferedImage hisEqual(BufferedImage originalImage)

&#xff08;1&#xff09;    功能说明&#xff1a;此函数实现直方图均衡化的功能

&#xff08;2&#xff09;    输入参数说明&#xff1a;originalImage为读入的图像

&#xff08;3&#xff09;    输出参数说明&#xff1a;获得对应的经过直方图均衡处理后的图像

&#xff08;4&#xff09;    算法流程&#xff1a;

遍历每个灰度值的像素个数huidunum[]——>

得到灰度值范围[0&#xff0c;当前灰度值]内的像素个数temp——>

该区间与总像素个数的比值——>

新灰度值&#61;原灰度值×比值——>

以新灰度值为R、G、B分量得到RGB值——>

新像素

    // 直方图均衡处理,不改变源图像

    private BufferedImage hisEqual(BufferedImage originalImage) {

        int alph, rgb, red, green, blue, gray;

        int width &#61; originalImage.getWidth();

        int height &#61; originalImage.getHeight();

        double temp &#61; 0.0;

 

        BufferedImage transimg &#61; new BufferedImage(width, height,

                originalImage.getType());// 创建一张同样大小的新图片

 

        for (int i &#61; 0; i <256; i&#43;&#43;) {//遍历灰度直方图

            temp &#61; 0.0;

            for (int j &#61; 0; j <&#61; i; j&#43;&#43;) {//

                temp &#61; (double) huidunum[j] &#43; temp;

            }

            temp &#61; (temp / (double) width) / (double) height;

            transhuidu[i] &#61; (int) (255.0 * temp);

        }

        for (int i &#61; 0; i

            for (int j &#61; 0; j

                rgb &#61; originalImage.getRGB(i, j);

 

                alph &#61; rgb >> 24;

                red &#61; (rgb & 0xff0000) >> 16;

                green &#61; (rgb & 0xff00) >> 8;

                blue &#61; (rgb & 0xff);

 

                gray &#61; (int) (red * 299 &#43; green * 587 &#43; blue * 114) / 1000;

                gray &#61; transhuidu[gray];// 新灰度值

                rgb &#61; (alph <<24)|(gray <<16)|(gray <<8) | gray;//新RGB值

                transimg.setRGB(i, j, rgb);

            }

        }

        return transimg;

    }

 

3、private void getRecommendRange(int width, int height, int huidunum[])

&#xff08;1&#xff09;    功能说明&#xff1a;获得像素集中分布的灰度值范围

&#xff08;2&#xff09;       输入参数说明&#xff1a;三个参数分别为图像宽、高、各灰度值的像素个数

&#xff08;3&#xff09;       输出参数说明&#xff1a;函数得到像素集中分别的灰度值范围&#xff0c;下界、上界分别存入全局变量recommendstart、recommendend

&#xff08;4&#xff09;    算法流程&#xff1a;

初始化待求区间为[0,255]——>

以i遍历huidunum[]——>

以j遍历huidunum[i&#xff0c;255]——>

以k遍历huidunum[i&#xff0c;j] 并统计此区间内像素总个数sum——>

若sum不少于总像素个数的85&#xff05;且区间[i&#xff0c;j]长度小于上次得到的区间长度&#xff0c;则取[i&#xff0c;j]。如此循环直到完。

 

    // 获得源图像中占大部分的连续灰度值区间 [recommendstart,recommendend]

    private void getRecommendRange(int width, int height, int huidunum[]) {

        double sum &#61; 0.0;

        recommendstart &#61; 0;

        recommendend &#61; 255;

        for (int i &#61; 0; i <256; i&#43;&#43;) {

            for (int j &#61; i; j <256; j&#43;&#43;) {

                sum &#61; 0.0;

                for (int k &#61; i; k <&#61; j; k&#43;&#43;) {

                    sum &#61; sum &#43; (double) huidunum[k];

                }

                if (sum > percentage * width * height) {

                    if (j - i <recommendend - recommendstart) {

                        recommendstart &#61; i;

                        recommendend &#61; j;

                    }

                }

            }

        }

}

 

 

4、private int getNewGrayValue(int f, int a, int b, int c, int d)

&#xff08;1&#xff09;    功能说明&#xff1a;此函数实现灰度值转换

&#xff08;2&#xff09;    输入参数说明&#xff1a;f为原灰度值&#xff0c;[b&#xff0c;c]为原图像灰度值集中范围&#xff0c;[c&#xff0c;d]为要拉伸到的范围&#xff0c;由用户输入

&#xff08;3&#xff09;    输出参数说明&#xff1a;函数得到变换后的新灰度值

&#xff08;4&#xff09;    算法流程&#xff1a;此部分就是 二(二)2的编程实现&#xff0c;比较简单不再赘述

 

    private int getNewGrayValue(int f, int a, int b, int c, int d) {

        int newGrayValue &#61; f;

        if (f

            newGrayValue &#61; (int) ((double) c * (double) f / (double) a);

        } else if (f > b) {

            newGrayValue &#61; (int) ((double) (f - b) * (double) (255 - d)

                    / (double) (255 - b) &#43; d);

        } else {

            newGrayValue &#61; (int) ((double) (f - a) * (double) (d - c)

                    / (double) (b - a) &#43; c);

        }

        return newGrayValue;

}

 

5、     private BufferedImage Linearstretch(BufferedImage originalImage, int newgraystart, int newgrayend)

&#xff08;1&#xff09;    功能说明&#xff1a;根据用户输入的灰度值范围[newgraystart, newgrayend]进行分段线性拉伸

&#xff08;2&#xff09;    输入参数说明&#xff1a;originalImage为原图像&#xff0c;[newgraystart, newgrayend]为用户输入的灰度值范围

&#xff08;3&#xff09;    输出参数说明&#xff1a;函数执行成功后得到原图像经分段线性拉伸后的图像

&#xff08;4&#xff09;   算法流程&#xff1a;

执行getRecommendRange函数获得原图像计组像素集中分布的灰度值范围——>

遍历原图像的像素——>

获得该像素的rgb值——>

获得rgb值的分量r、g、b——>

获得灰度值gray——>

执行getNewGrayValue函数获得对应的新灰度值——>

以gray为三个分量获得新rgb值——>

得到新像素

 

private BufferedImage Linearstretch(BufferedImage originalImage,

            int newgraystart, int newgrayend) {

        int newhuidunum[] &#61; new int[256];

 

        int alph, rgb, red, green, blue, gray;

        int width &#61; originalImage.getWidth();

        int height &#61; originalImage.getHeight();

 

        BufferedImage transimg &#61; new BufferedImage(width, height,

                originalImage.getType());

 

        // 获得灰度大部分集中的范围 [recommendstart, recommendend]

        getRecommendRange(width, height, huidunum);

 

        for (int i &#61; 0; i

            for (int j &#61; 0; j

                rgb &#61; originalImage.getRGB(i, j);

 

                alph &#61; rgb >> 24;

                red &#61; (rgb & 0xff0000) >> 16;

                green &#61; (rgb & 0xff00) >> 8;

                blue &#61; (rgb & 0xff);

 

                gray &#61; (int) (red * 299 &#43; green * 587 &#43; blue * 114) / 1000;

 

                // 根据拉伸变换得到新的灰度值

                gray &#61; getNewGrayValue(gray, recommendstart, recommendend,

                        newgraystart, newgrayend);

                rgb &#61; (alph <<24) | (gray <<16) | (gray <<8) | gray;

                transimg.setRGB(i, j, rgb);

                newhuidunum[gray]&#43;&#43;;

            }

        }

        return transimg;

}

四、 实验

&#xff08;一&#xff09;灰度直方图均衡

  1. 1.      实验与结果


 


 

  1. 2.      结果分析

图像经过直方图均衡处理后&#xff0c;图像的对比度会增强&#xff0c;特别是对于背景或前景都太亮或太暗的图像非常有用。

均衡化后的各灰度级更加均衡&#xff0c;对于灰度范围小&#xff0c;直方图分布极不均匀的图像&#xff0c;可人为的适当的扩大灰度范围&#xff0c;均衡化后能取得较好的层次感&#xff0c;使图像信息变得更清晰。

&#xff08;二&#xff09;分段线性拉伸

&#xff08;1&#xff09;      
实验与结果


 

 

 

&#xff08;2&#xff09;       结果分析

从以上实验及其结果对比可看出&#xff0c;线性变换可以把原图像相对较集中的灰度级拉伸到到指定的灰度级范围&#xff0c;当灰度级范围比原范围低时&#xff0c;图像将变暗&#xff0c;反之则变亮。

五、 结论

直方图均衡和线性拉伸是对数字图像进行处理的方法中的两种&#xff0c;是点运算。

直方图均衡处理可以使所有灰度级出现的相对概率相同&#xff0c;经过直方图均衡处理后&#xff0c;图像的对比度会增强&#xff0c;会更有层次感&#xff0c;特别是对于背景或前景都太亮或太暗的图像非常有用。

线性拉伸可以把原图像集中的灰度值区间拉伸到指定的区间上&#xff0c;这样可以突出感兴趣的目标或灰度区间&#xff0c;相对抑制不感兴趣的灰度区间。

参考文献

[1] 冈萨雷斯. 数字图像处理[M]. 北京:电子工业出版社, 2011.

 



推荐阅读
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • Hibernate延迟加载深入分析-集合属性的延迟加载策略
    本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • tcpdump 4.5.1 crash 深入分析
    tcpdump 4.5.1 crash 深入分析 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 如何实现JDK版本的切换功能,解决开发环境冲突问题
    本文介绍了在开发过程中遇到JDK版本冲突的情况,以及如何通过修改环境变量实现JDK版本的切换功能,解决开发环境冲突的问题。通过合理的切换环境,可以更好地进行项目开发。同时,提醒读者注意不仅限于1.7和1.8版本的转换,还要适应不同项目和个人开发习惯的需求。 ... [详细]
  • 使用freemaker生成Java代码的步骤及示例代码
    本文介绍了使用freemaker这个jar包生成Java代码的步骤,通过提前编辑好的模板,可以避免写重复代码。首先需要在springboot的pom.xml文件中加入freemaker的依赖包。然后编写模板,定义要生成的Java类的属性和方法。最后编写生成代码的类,通过加载模板文件和数据模型,生成Java代码文件。本文提供了示例代码,并展示了文件目录结构。 ... [详细]
  • 本文介绍了在Ubuntu 11.10 x64环境下安装Android开发环境的步骤,并提供了解决常见问题的方法。其中包括安装Eclipse的ADT插件、解决缺少GEF插件的问题以及解决无法找到'userdata.img'文件的问题。此外,还提供了相关插件和系统镜像的下载链接。 ... [详细]
  • 流数据流和IO流的使用及应用
    本文介绍了流数据流和IO流的基本概念和用法,包括输入流、输出流、字节流、字符流、缓冲区等。同时还介绍了异常处理和常用的流类,如FileReader、FileWriter、FileInputStream、FileOutputStream、OutputStreamWriter、InputStreamReader、BufferedReader、BufferedWriter等。此外,还介绍了系统流和标准流的使用。 ... [详细]
  • 本文介绍了使用C++Builder实现获取USB优盘序列号的方法,包括相关的代码和说明。通过该方法,可以获取指定盘符的USB优盘序列号,并将其存放在缓冲中。该方法可以在Windows系统中有效地获取USB优盘序列号,并且适用于C++Builder开发环境。 ... [详细]
author-avatar
小新亚笔
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有