通常我们生活中遇到的图像,无论是jpg、还是png或者bmp格式,一般都是8位的(每个通道的像素值范围是0-255),但是随着一些硬件的发展,在很多行业比如医疗、红外、航拍等一些场景下,拥有更宽的量化范围的图像也越来越常见,比如10位(带宽1024)、12位(带宽4096)、14位(带宽16384)以及16位(带宽32768)的图像,当然还有以浮点数保存的高动态图像(hdr格式的那种),但是目前大部分的显示器还是只支持8位图像的显示,因此,对于这一类图像,一个很重要的问题就是如何将他们的数据量化到0到255之间,而且尽量的保留更多的细节信息,这也就是常见的HDR到LDR的过程。 在我前面的博客里其实也有讲到这方面的信息,本文再尝试将直方图均衡化引入到这个过程中。

首先,我们统一一下由一组ushort数据(带宽是10、12、14、16的Raw图像,都可以用ushort数据类型表示)直接量化为8位显示的函数,这样我们的处理就可以集中在原始的ushort数据经过算法处理后得到新的ushort数据的过程。这个函数简单如下所示:

  1. // 这个只是个辅助用来显示的函数
  2. int IM_ConvetUshortToByte(unsigned short *Src, unsigned char *Dest, int Width, int Height, int Stride, int WindowWidth, int WindowLevel)
  3. {
  4. int Channel = Stride / Width;
  5. int Min = WindowLevel - WindowWidth / 2;
  6. int Max = WindowLevel + WindowWidth / 2;
  7. if (Min < 0) Min = 0;
  8. if (Max > 65535) Max = 65535;
  9. int Diff = Max - Min;
  10. if (Diff == 0)
  11. {
  12. memset(Dest, Max, Height * Stride * sizeof(unsigned char));
  13. }
  14. else
  15. {
  16. unsigned char Table[65536];
  17. for (int X = 0; X < 65536; X++)
  18. {
  19. if (X < Min)
  20. Table[X] = 0;
  21. else if (X > Max)
  22. Table[X] = 255;
  23. else
  24. Table[X] = IM_ClampToByte((X - Min) * 255 / Diff);
  25. }
  26. for (int Y = 0; Y < Height; Y++)
  27. {
  28. unsigned short *LinePS = Src + Y * Width * Channel;
  29. unsigned char *LinePD = Dest + Y * Stride;
  30. for (int X = 0; X < Width * Channel; X++)
  31. {
  32. LinePD[X] = Table[LinePS[X]];
  33. }
  34. }
  35. }
  36. return IM_STATUS_OK;
  37. }

   其中的WindowWidth表示窗宽,WindowLevel表示窗位,这个其实是借助了医学图像上的一些概念,对于普通的RAW图像,比如12位,我们通常就认为WindowWidth = 1 << 12 = 4096,而WindowLevel则就为窗宽的一半。

一般来说,RAW图像中的数据每一行是没有冗余量的,即没有BMP位图中所谓的扫描行对齐的概念。所以可以直接遍历每一个数据。

那么我们来看看如何把普通的直方图均衡化算法利用到RAW图像中来。

以灰度图为例,如果已经统计了图像的直方图,则直方图均衡化的新的隐射曲线由以下代码获取:

  1. for (int Y = 0, Num = 0; Y < 256; Y++)
  2. {
  3. Num = Num + Histgram[Y];
  4. Table[Y] = (unsigned char)(((float)Num * 255) / (Width * Height)); // 注意(float)强制转换的位置,否则对于大图就溢出了,2014.11.1修正
  5. }

简单的,扩展到ushort类型的RAW图像,我们也可以用以下的类似代码搞定:

  1. for (int Y = 0, Num = 0; Y < WindowWidth; Y++)
  2. {
  3. Num = Num + Histgram[Y];
  4. Table[Y] = (unsigned short)(((float)Num * WindowWidth) / (Width * Height)); // 注意(float)强制转换的位置,否则对于大图就溢出了,2014.11.1修正
  5. }

关于这个WindowWidth,我觉得一般可以由两种方法得到,一个是我们已经知道了这个硬件生成的图像是多少位的,常见的就是10、12、14、16等,这个时候WindowWidth可以直接指定,而如果只有RAW数据,一种方式就是根据数据的最大值来确定WindowWidth,即取大于最大值的2的整数次幂的那个值。
      如下代码所示:

  1. ushort MaxValue = IM_GetMaxValue(ImageData, ImageWidth * ImageHeight * ImageChannel);
  2. int ProperWindowWidth = 1;
  3. while (ProperWindowWidth < MaxValue)
  4. ProperWindowWidth *= 2;
  5. WindowWidth = ProperWindowWidth;
  6. WindowLevel = WindowWidth / 2;

这个简单的数据范围的调整,我们看下一些效果:

     

      

a、RAW数据直接ConvetUshortToByte的8位结果图            b、直方图均衡后的RAW数据转换为8位的效果图           c、对8位的a图直接在直方图均衡后的结果图

通过比较可以看到确实还是有明显的增强效果的,但是似乎有过曝的现象。

我们可以仿照一种强化的基于局部直方图裁剪均衡化的对比度调节算法 或者限制对比度自适应直方图均衡化算法原理、实现及效果 文中的方法将局部直方图均衡化引入到16位中,尝试看看效果是否有改善,这里不多谈,只说下我遇到的几个问题。

一个是在ClipHistogram 这个函数的过程中,我们发现往往会出现这个函数陷入死循环的结果,特备是对于12位以上的图像,因此,这个可能需要其他的一些改进方案。

二个是我们还可以学习【算法随记四】自动色阶、对比度、直方图均衡等算法的一些小改进 一文中的getWeightedValue函数,即对获取的直方图数据开平方,起到一定的压缩作用,这个可以明显的改善上述的曝光效果。

另外,同样的道理,在局部算法里,还可以不用直方图均衡化算法,可以使用任何其他的基于直方图的调整基数,比如自动色剂等等。

   

a、RAW数据直接ConvetUshortToByte的8位结果图                          b、局部压缩直方图均衡后的RAW数据转换为8位的效果图           

很明显这样处理后的效果要好很多。细节、曝光等等都较为合适。

关于16位RAW图像,本人开发了一个简易的增强和处理程序,可在https://files.cnblogs.com/files/Imageshop/Optimization_Demo_16.rar下载测试。

其他相关链接:

【16位RAW图像处理一】:基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。

【16位RAW图像处理二】:一种自适应对数映射的高对比度图像显示技术及其速度优化。

【16位RAW图像处理三】直方图均衡化及局部直方图均衡用于16位图像的细节增强。的更多相关文章

  1. 图像处理之直方图均衡化及C源码实现

    1 直方图均衡化(Histogram Equalization)简介 图像对比度增强的方法可以分成两类:一类是直接对比度增强方法;另一类是间接对比度增强方法.直方图拉伸和直方图均衡化是两种最常见的间接 ...

  2. OpenCV-跟我一起学数字图像处理之直方图均衡化

    从这篇博文开始,小生正式从一个毫不相干专业转投数字图像处理.废话不多说了,talk is cheap. show me the code. 直方图均衡化目的 由于一些图像灰度的分布过于集中,这样会导致 ...

  3. 机器学习进阶-直方图与傅里叶变化-直方图均衡化 1.cv2.equalizeHist(进行直方图均衡化) 2. cv2.createCLAHA(用于生成自适应均衡化图像)

    1. cv2.equalizeHist(img)  # 表示进行直方图均衡化 参数说明:img表示输入的图片 2.cv2.createCLAHA(clipLimit=8.0, titleGridSiz ...

  4. 彩色图像的直方图均衡化matlab代码

    彩色图像的直方图均衡化 - YangYudong2014的专栏 - CSDN博客 http://blog.csdn.net/yangyudong2014/article/details/4051503 ...

  5. 16位/32位/64位CPU的位究竟是说啥

    平时,我们谈论CPU,都会说某程序是32位编译,可以跑在32位机或64位机,或则是在下载某些开源包时,也分32位CPU版本或64CPU位版本,又或者在看计算机组成相关书籍时,特别时谈到X86 CPU时 ...

  6. [Effective JavaScript 笔记] 第7条:视字符串为16位的代码单元序列

    Unicode编码,基础:它为世界上所有的文字系统的每个字符单位分配一个唯一的整数,该整数介于0~1114111之间,在Unicode术语中称为代码点(code point). 和其它字符编码几乎没有 ...

  7. 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16

    一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...

  8. opencv——图像的灰度处理(线性变换/拉伸/直方图/均衡化)

    实验内容及实验原理: 1.灰度的线性变换 灰度的线性变换就是将图像中所有的点的灰度按照线性灰度变换函数进行变换.该线性灰度变换函数是一个一维线性函数:f(x)=a*x+b 其中参数a为线性函数的斜率, ...

  9. 图像处理 Matlab实现线性点运算、非线性点运算、点运算与直方图、直方图均衡化

    今天,我们学习了直方图.于是乎,回来我就用matlab代码实现一下.昨天受到道路检测老师课上一个内容的影响(对于道路裂缝的检测,我突发奇想,如果对于道路图像进行操作,是否能够让裂缝与道路分离,使得图像 ...

随机推荐

  1. 结构感知图像修复:ICCV2019论文解析

    结构感知图像修复:ICCV2019论文解析 StructureFlow: Image Inpainting via Structure-aware Appearance Flow 论文链接: http ...

  2. NVIDIA Turing Architecture架构设计(上)

    NVIDIA Turing Architecture架构设计(上) 在游戏市场持续增长和对更好的 3D 图形的永不满足的需求的推动下, NVIDIA 已经将 GPU 发展成为许多计算密集型应用的世界领 ...

  3. 特斯拉Tesla Model 3整体架构解析(下)

    特斯拉Tesla Model 3整体架构解析(中) Tesla Computer Unit 特斯拉已经开发了一个由自动驾驶仪和信息计算机组成的定制"液冷双计算平台"."他 ...

  4. 关于switch语句的使用方法---正在苦学java代码的新手之菜鸟日记

    输入月份与年份,判断所输入的月份有多少天. switch支持和不支持的类型 支持的类型 int 类型 short 类型 byte 类型 char 类型 enum (枚举)类型 (java5.0 之后支 ...

  5. Java 到底是值传递还是引用传递?

    关于这个问题,引发过很多广泛的讨论,看来很多程序员对于这个问题的理解都不尽相同,甚至很多人理解的是错误的.还有的人可能知道Java中的参数传递是值传递,但是说不出来为什么. 在开始深入讲解之前,有必要 ...

  6. STM32学习笔记-NVIC中断知识点

    STM32学习笔记-NVIC中断知识点总结 中断优先级设置步骤 1. 系统运行后先设置中断优先级分组 函数:void NVIC_PriorityGroupConfig(uint32_tNVIC_Pri ...

  7. CMake 两种变量原理

    目录 [TOC] 1.两种变量的定义参考 2.两种变量的作用域原理及使用 1.Normal Variables (1).包含 add_subdirectory().function().(本质是值拷贝 ...

  8. linux下 大日志文件查看与搜索---less

    场景 有一个几十m的大日志文件,里边的记录是按时间排序的. 现在需要找到其中,不知道在什么位置的一条错误消息.这时候,想把内容拷出来都费劲,就算拷出来了,一般的编辑器也难以hold住这么大的文件.这时 ...

  9. Redis之内存优化

    Redis所有的数据都存在内存中,当前内存虽然越来越便宜,但跟廉价的硬盘相比成本还是比较昂贵,因此如何高效利用Redis内存变得非常重要.高效利用Redis内存首先需要理解Redis内存消耗在哪里,如 ...

  10. 《手把手教你》系列技巧篇(六)-java+ selenium自动化测试-阅读selenium源码(详细教程)

    1.简介 前面几篇基础系列文章,足够你迈进了Selenium门槛,再不济你也至少知道如何写你第一个基于Java的Selenium自动化测试脚本.接下来宏哥介绍Selenium技巧篇,主要是介绍一些常用 ...