热门标签 | 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.

 



推荐阅读
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 判断数组是否全为0_连续子数组的最大和的解题思路及代码方法一_动态规划
    本文介绍了判断数组是否全为0以及求解连续子数组的最大和的解题思路及代码方法一,即动态规划。通过动态规划的方法,可以找出连续子数组的最大和,具体思路是尽量选择正数的部分,遇到负数则不选择进去,遇到正数则保留并继续考察。本文给出了状态定义和状态转移方程,并提供了具体的代码实现。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
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社区 版权所有