前景介绍
借助OpenCV的能力做图像增强,前提当然是要集成OpenCV,这里不做集成环境的介绍,以下只介绍图像增强的方式,以及每种方式对应适合的场景。
图像灰度
把白色与黑色之间按对数关系分为若干等级,称为灰度。灰度分为256阶(0~255,0:黑色,255:白色)。用灰度表示的图像称作灰度图.\
一幅完整的图像,是由红色绿色蓝色三个通道组成的。
红色、绿色、蓝色三个通道的缩览图都是以灰度显示的。用不同的灰度色阶来表示“ 红,绿,蓝”在图像中的比重。通道中的纯白,代表了该色光在此处为最高亮度,亮度色阶是255;相反,纯黑代表了色光在该处的最低亮度,亮度色阶为0。
处理图像,实际上是改变每个像素点的灰度值以达到我们要得到的图像。下边介绍四种方式针对不同图像环境来做图像增强。
图像直方图
图像直方图是统计图像中每个灰度值的个数,之后将图像灰度值作为横轴,将图像灰度值的个数或者灰度值在图像中所占的比例作为纵轴绘制的统计图。
图像增强
基于直方图均衡化 图像增强
直方图均衡化是通过调整图像的灰阶分布,使得在0~255灰阶上的分布更加均衡,提高了图像的对比度,达到改善图像主观视觉效果的目的。对比度较低的图像适合使用直方图均衡化方法来增强图像细节。
fun rectangleEnhance(example: Mat): Mat {
// 转换
if (example.type() == CvType.CV_8UC4) {
Imgproc.cvtColor(example, example, Imgproc.COLOR_BGRA2BGR)
}
// 通道分离
val channels = arrayListOf<Mat>()
Core.split(example, channels)
channels.forEachIndexed { _, mat ->
Imgproc.equalizeHist(mat, mat)
}
// 图像通道合并
val dst = Mat()
Core.merge(channels, dst)
return dst
}
效果
基于拉普拉斯算子的图像增强
使用中心为4的8邻域拉普拉斯算子与图像卷积可以达到锐化增强图像的目的,拉普拉斯算子可以增强局部的图像对比度。适合图像整体对比度不明显的图像做增强,突出局部。
fun laplaceEnhance(example: Mat): Mat {
// 拉普拉斯算子
val laplaceMat = Mat(3, 3, CvType.CV_32F)
laplaceMat.put(0, 0, 0.0, -1.0, 0.0, 0.0, 4.0, 0.0, 0.0, -1.0, 0.0)
val dst = Mat()
// 做卷积运算
Imgproc.filter2D(example, dst, -1, laplaceMat)
return dst
}
效果
基于对数log变换的增强
对数变换可以将图像的低灰度值部分扩展,显示出低灰度部分更多的细节,将其高灰度值部分压缩,减少高灰度值部分的细节,从而达到强调图像低灰度部分的目的。对于不同的底数,底数越大,对低灰度部分的扩展就越强,对高灰度部分的压缩也就越强。
extern "C"
JNIEXPORT void JNICALL
Java_com_dream_androidcv_java_NativeActivity_logEnhance(JNIEnv *env, jobject thiz,
jobject bitmap_in, jobject bitmap_out) {
cv::Mat matBitmapIn;
bool toMatrix = BitmapToMatrix(env, bitmap_in, matBitmapIn);
if (!toMatrix) return;
Mat imageLog(matBitmapIn.size(), CV_32FC3);
for (int i = 0; i < matBitmapIn.rows; i++) {
for (int j = 0; j < matBitmapIn.cols; j++) {
imageLog.at<Vec3f>(i, j)[0] = log(0.5+matBitmapIn.at<Vec3b>(i, j)[0]);
imageLog.at<Vec3f>(i, j)[1] = log(0.5+matBitmapIn.at<Vec3b>(i, j)[1]);
imageLog.at<Vec3f>(i, j)[2] = log(0.5+matBitmapIn.at<Vec3b>(i, j)[2]);
}
}
//归一化到0~255
normalize(imageLog, imageLog, 0, 255, CV_MINMAX);
//转换成8bit图像显示
convertScaleAbs(imageLog, imageLog);
cvtColor(imageLog,imageLog,COLOR_BGR2RGB);
MatrixToBitmap(env, imageLog, bitmap_out);
}
效果
基于伽马变换的图像增强
γ值以1为分界,值越小,对图像低灰度部分的扩展作用就越强,值越大,对图像高灰度部分的扩展作用就越强,通过不同的γ值,就可以达到增强低灰度或高灰度部分细节的作用。伽马变换对于图像对比度偏低,并且整体亮度值偏高(对于于相机过曝)情况下的图像增强效果明显。
extern "C"
JNIEXPORT void JNICALL
Java_com_dream_androidcv_java_NativeActivity_gammaEnhance(JNIEnv *env, jobject thiz,
jobject bitmap_in, jobject bitmap_out) {
cv::Mat image;
bool toMatrix = BitmapToMatrix(env, bitmap_in, image);
if (!toMatrix) return;
Mat imageGamma(image.size(), CV_32FC3);
for (int i = 0; i < image.rows; i++)
{
for (int j = 0; j < image.cols; j++)
{
imageGamma.at<Vec3f>(i, j)[0] = (image.at<Vec3b>(i, j)[0])*(image.at<Vec3b>(i, j)[0])*(image.at<Vec3b>(i, j)[0]);
imageGamma.at<Vec3f>(i, j)[1] = (image.at<Vec3b>(i, j)[1])*(image.at<Vec3b>(i, j)[1])*(image.at<Vec3b>(i, j)[1]);
imageGamma.at<Vec3f>(i, j)[2] = (image.at<Vec3b>(i, j)[2])*(image.at<Vec3b>(i, j)[2])*(image.at<Vec3b>(i, j)[2]);
}
}
//归一化到0~255
normalize(imageGamma, imageGamma, 0, 255, CV_MINMAX);
//转换成8bit图像显示
convertScaleAbs(imageGamma, imageGamma);
// 将Mat的BGR转成RGB
cvtColor(imageGamma,imageGamma,COLOR_BGR2RGB);
MatrixToBitmap(env, imageGamma, bitmap_out);
}
效果
总结
并不是所有的图像都适合进行增强,图像增强的本质:对图像的灰度色阶进行更加平滑的过度,以此来达到图像增强的目的(通用型)。那么针对特定场景,对灰度色阶进行特定的处理。对于图像灰度色阶的分布检测,获取一个合适的阈值是判断图像是否需要增强的关键。
- 直方图均衡化适合对比度较低的图像用来增强图像细节。
- 拉普拉斯算子卷积适合图像整体对比度不明显的图像做局部增强,突出局部特征。
- 对数变换适合调高图像的亮度,拉升高色阶,压缩低色阶。
- 伽马变换适合调低图像的亮度,压缩高色阶,拉升低色阶。