【android opencv学习笔记】Day 15: 直方图均衡化
·

OpenCV 直方图均衡化
在图像处理中,直方图均衡化是一种强大的对比度增强技术,它通过调整图像的灰度分布,让像素值更均匀地分布在 0~255 全范围,从而显著提升图像的细节和清晰度。
本文将从原理到实战,带你深入理解并实现基于 OpenCV cv::equalizeHist 的直方图均衡化功能。
什么是直方图均衡化?
上一节我们介绍了直方图拉伸,它通过线性映射扩展图像的灰度范围。
但很多时候,图像的问题并非灰度范围狭窄,而是像素分布不均匀——比如中等灰度像素过多,而亮部和暗部像素稀少,导致图像灰蒙蒙、细节不清晰。
直方图均衡化的核心思想,就是让图像的直方图尽可能“平坦”,让所有灰度级都得到充分利用:
- 灰度级少的像素(暗部/亮部)会被“拉高”,扩大其分布范围
- 灰度级多的像素(中间调)会被“压缩”,降低其分布密度
- 最终实现像素分布的均衡化,图像细节和对比度显著提升
OpenCV 核心函数:cv::equalizeHist
OpenCV 提供了现成的 cv::equalizeHist 函数,可一键实现灰度图像的直方图均衡化:
void equalizeHist(InputArray src, OutputArray dst);
src:输入的单通道灰度图像(必须是 8 位CV_8UC1类型)dst:输出的均衡化图像(与输入图像尺寸、类型相同)
实现原理:从直方图到均衡化映射
直方图均衡化的本质,是基于累积直方图构建查找表(LUT),实现像素值的非线性映射。
1. 累积直方图
累积直方图表示“强度值小于或等于 i 的像素数量”,公式为:
p[i]=∑k=0ih[k]
p[i] = \sum_{k=0}^{i} h[k]
p[i]=k=0∑ih[k]
其中 h[k] 是原始直方图中灰度级 k 的像素数量。
2. 均衡化映射公式
利用累积直方图构建查找表,将原始灰度值映射到新的灰度值:
lookup.at<uchar>(i) = static_cast<uchar>(255.0 * p[i] / image.total());
p[i]:累积直方图中灰度级i对应的像素总数image.total():图像的总像素数255.0 * p[i] / image.total():将累积百分比映射到 0~255 范围
Android Native JNI 完整实现
1. Native 层 native-lib.cpp
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <android/bitmap.h>
using namespace cv;
using namespace std;
// ==========================
// 工具:Bitmap ↔ Mat 彩色图
// ==========================
Mat bitmapToMat(JNIEnv *env, jobject bitmap) {
AndroidBitmapInfo info;
void *pixels;
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
Mat rgba(info.height, info.width, CV_8UC4, pixels);
Mat bgr;
cvtColor(rgba, bgr, COLOR_RGBA2BGR);
AndroidBitmap_unlockPixels(env, bitmap);
return bgr;
}
void matToBitmap(JNIEnv *env, const Mat &mat, jobject bitmap) {
AndroidBitmapInfo info;
void *pixels;
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
Mat rgba;
cvtColor(mat, rgba, COLOR_BGR2RGBA);
memcpy(pixels, rgba.data, info.height * info.width * 4);
AndroidBitmap_unlockPixels(env, bitmap);
}
// ==========================
// 灰度直方图均衡化
// ==========================
Mat equalizeImage(const Mat &gray) {
Mat result;
equalizeHist(gray, result);
return result;
}
// ==========================
// 你提供的:彩色直方图均衡化
// ==========================
Mat equalizeColorImage(const Mat &src) {
Mat ycrcb;
cvtColor(src, ycrcb, COLOR_BGR2YCrCb);
vector<Mat> channels;
split(ycrcb, channels);
// 仅对亮度通道均衡化
equalizeHist(channels[0], channels[0]);
merge(channels, ycrcb);
Mat result;
cvtColor(ycrcb, result, COLOR_YCrCb2BGR);
return result;
}
// ==========================
// JNI 接口:一次返回两张效果图
// ==========================
extern "C" JNIEXPORT void JNICALL
Java_com_example_equalize_MainActivity_processEqualize(
JNIEnv *env,
jobject thiz,
jobject srcBitmap,
jobject outGrayBitmap,
jobject outColorBitmap
) {
Mat src = bitmapToMat(env, srcBitmap);
// 1. 转灰度 + 均衡
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat eqGray = equalizeImage(gray);
// 2. 彩色均衡
Mat eqColor = equalizeColorImage(src);
// 回传
matToBitmap(env, eqGray, outGrayBitmap);
matToBitmap(env, eqColor, outColorBitmap);
}
2. Kotlin 层 MainActivity.kt
package com.example.equalize
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
companion object {
init {
System.loadLibrary("native-lib")
}
}
private external fun processEqualize(
src: Bitmap,
outGray: Bitmap,
outColor: Bitmap
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 原图
val src = BitmapFactory.decodeResource(resources, R.drawable.test)
// 创建输出位图
val equalizedGray = Bitmap.createBitmap(src.width, src.height, Bitmap.Config.ARGB_8888)
val equalizedColor = Bitmap.createBitmap(src.width, src.height, Bitmap.Config.ARGB_8888)
// 调用 C++
processEqualize(src, equalizedGray, equalizedColor)
// 显示
findViewById<ImageView>(R.id.iv_original).setImageBitmap(src)
findViewById<ImageView>(R.id.iv_equalized_gray).setImageBitmap(equalizedGray)
findViewById<ImageView>(R.id.iv_equalized_color).setImageBitmap(equalizedColor)
}
}
3. 布局文件 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="3">
<!-- 原图 -->
<ImageView
android:id="@+id/iv_original"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="centerCrop"/>
<!-- 灰度均衡化 -->
<ImageView
android:id="@+id/iv_equalized_gray"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="centerCrop"/>
<!-- 彩色均衡化 -->
<ImageView
android:id="@+id/iv_equalized_color"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:scaleType="centerCrop"/>
</LinearLayout>

效果说明
- 原图:可能存在对比度不足、细节模糊的问题,像素分布集中在中间调。
- 均衡化图像:像素分布更均匀,暗部细节和亮部细节都得到了增强,图像整体对比度显著提升。
直方图均衡化实现原理
直方图均衡化通过以下步骤实现图像增强:
- 计算原始直方图:统计每个灰度级的像素数量。
- 计算累积直方图:得到每个灰度级的累积像素数。
- 构建均衡化查找表:将累积百分比映射到 0~255 范围,生成新的像素映射关系。
- 应用映射:使用查找表对图像进行像素重映射,实现直方图均衡化。
与直方图拉伸相比,直方图均衡化是一种非线性增强方法,能更有效地处理像素分布不均匀的图像,提升细节表现。
总结
直方图均衡化是一种高效的图像增强技术,通过调整像素分布,让图像对比度更均匀、细节更清晰。本文从原理到实战,完整实现了:
- 灰度图像直方图均衡化
- Android Native JNI 完整移植方案
- 彩色图像均衡化扩展

更多推荐

所有评论(0)