本节介绍基于OpenCV编写图像窗宽窗位动态调节程序,主要实现两个进度条分别控制窗宽、窗位,将高位编码图像转化为可供显示的uint8数据类型。此处给出思路和许多功能模块的调用方法,完整代码可以在恰当的时机放出。
图像读取
对于一般图像来说读取图像只需简单调用函数,但注意这里我们读取的不是寻常8位三通道/单通道图像,而是16位图像,因此最好这样:
frame = imread("exp2_2.png", IMREAD_UNCHANGED);//无更改式读取原图
之后我们建立一个空间存放转换后的图像
Mat show_frame = Mat::zeros(frame.rows, frame.cols, CV_8UC1);
OpenCV中矩阵类型与内部数据类型对应关系
通式:CV_<bit_depth>(S|U|F)C<number_of_channels>
如果看不懂的话:
这个对应关系非常重要!
矩阵类型 | 常量代号 | 内部元素的数据类型 |
8位无符号整型单通道矩阵 | CV_8UC1 | uchar |
8位无符号整型3通道矩阵 | CV_8UC3 | Vec3b、内部uchar |
16位整型单通道矩阵 | CV_16SC1 | short |
16位无符号整型单通道矩阵 | CV_16UC1 | ushort |
32位浮点型单通道矩阵 | CV_32FC1 | float |
64位浮点型三通道矩阵 | CV_64FC3 | Vec3b、double |
OpenCV矩阵逐像素访问方法
在本程序中我们需要对图像矩阵内数据逐一遍历并操作,那么如何访问矩阵内特定元素呢,这里我们介绍at()方法,调用方式为:
对于单通道图像:
图像变量名.at<元素类型>(行,列)
对于多通道图像:
图像变量名.at<元素类型>(行,列)[通道]
注意上述元素类型,必须与图像矩阵的类型相对应,对应方式见上一节
例如对于正常的三通道RGB图像,我们可以这样调用元素:
uchar a = img.at<Vec3b>(y, x)[c]//y为行,x为列,c为通道,得到的结果是uchar类型
但对于正常的单通道灰度图,我们要写
uchar a = img.at<uchar>(y, x)//y为行,x为列,得到的结果是uchar类型
但对于其他特殊图像,比如16位无符号单通道图像,我们要这样写:
ushort a = img.at<ushort>(y, x)//y为行,x为列,得到的结果是ushort类型
一定要注意元素类型与矩阵类型的对应性,16位无符号整型单通道矩阵(CV_16UC1)内的元数类型为ushort,写成其他类型,OpenCV在内存中会多读或少读字节,自然得不到正确的值。
把上述语句放在两层for循环中就能遍历图像了
OpenCV中饱和截断方法
在本文设计的任务“窗宽窗位调整”中,要对输入图像逐像素做这样的映射,必然涉及小于0或超出255的像素值的转换,可以使用OpenCV自带工具,如
saturate_cast<uchar>(a);
可以把变量a的值饱和截断,转换成uchar类型,类似Python中numpy.clip()方法
窗宽窗位变换函数
有了以上知识,本题思路已经很明确了
- 使用合适的方法打开高位特殊图像
- 建立8UC1类型空Mat
- 逐像素访问图像,将原有像素值线性变换,使用(L-W/2,0)和(L+W/2,255)拟合变换曲线
- 对变换后结果饱和截断,存入8UC1的Mat
- 显示新Mat
当然为了展示效果,我们可以把上述步骤放在while循环里面,加上两个滑动条调节窗宽窗位,每次循环过程中根据窗宽窗位计算显示结果。