在 Android OpenCV 中,直方图(Histogram) 是一种用于表示图像像素分布的工具,广泛用于 图像分析、对比度增强、特征提取、颜色检测 等任务。
1. 直方图的基本概念
直方图是一个统计图,表示图像中不同灰度值(或颜色值)出现的频率。常见类型:
• 灰度直方图(Grayscale Histogram):统计 0~255 灰度级 像素的分布。
• 彩色直方图(Color Histogram):分别统计 RGB 三通道的分布情况。
直方图常用于:
-
图像对比度分析(判断是否曝光过度、过暗)
-
图像增强(直方图均衡化)
-
颜色分布分析(颜色识别、目标检测)
-
特征匹配(直方图相似度计算)
2. OpenCV 计算直方图的方法
在 OpenCV 中,计算直方图使用 Imgproc.calcHist() 方法:
Imgproc.calcHist(List<Mat> images, int[] channels, Mat mask, Mat hist, int[] histSize, float[] ranges);
参数解析
参数 | 作用 |
---|---|
images | 输入图像(通常是 Mat 类型,需转换为 List) |
channels | 指定计算哪个通道(0=灰度,0/1/2=RGB 通道) |
mask | 掩码(可选),用于计算特定区域的直方图 |
hist | 输出直方图(Mat 类型) |
histSize | 直方图的 bin(柱形数) ,通常设为 256 |
ranges | 灰度或颜色范围(通常 0~255) |
3. 计算灰度图直方图(Grayscale Histogram)
示例:计算灰度直方图
public class HistogramActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_histogram);
ImageView imageView = findViewById(R.id.imageView2);
// 三通达彩色直方图============================
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b8);
Mat src = new Mat();
Utils.bitmapToMat(bitmap, src);
// 拆分通道:B、G、R(注意顺序)
List<Mat> bgrPlanes = new ArrayList<>();
Core.split(src, bgrPlanes);
// 分别计算三个通道的直方图
List<Mat> histList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
List<Mat> mats=new ArrayList<>();
mats.add(bgrPlanes.get(i));
Mat hist = new Mat();
Imgproc.calcHist(
mats,
new MatOfInt(0),
new Mat(),
hist,
new MatOfInt(256),
new MatOfFloat(0, 256)
);
histList.add(hist);
}
// 绘制三通道直方图
Mat histImage = drawColorHistogram(histList, 256, 256);
Bitmap histBitmap = Bitmap.createBitmap(histImage.cols(), histImage.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(histImage, histBitmap);
imageView.setImageBitmap(histBitmap);
//普通直方图========================================================
// // 加载图片
// Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b8);
// Mat src = new Mat();
// Utils.bitmapToMat(bitmap, src);
//
// // 转换为灰度图
// Mat gray = new Mat();
// Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
//
// // 计算直方图
// Mat hist = new Mat();
// List<Mat> images = new ArrayList<>();
// images.add(gray);
// Imgproc.calcHist(images, new MatOfInt(0), new Mat(), hist, new MatOfInt(256), new MatOfFloat(0,256));
//
// // 画直方图
// Mat histImage = drawHistogram(hist, 256, 256);
// Bitmap histBitmap = Bitmap.createBitmap(histImage.cols(), histImage.rows(), Bitmap.Config.ARGB_8888);
// Utils.matToBitmap(histImage, histBitmap);
// imageView.setImageBitmap(histBitmap);
}
/**
* 普通直方图
* @param hist
* @param width
* @param height
* @return
*/
private Mat drawHistogram(Mat hist, int width, int height) {
Mat histImage = new Mat(height, width, CvType.CV_8UC3, new Scalar(255, 255, 255));
Core.normalize(hist, hist, 0, histImage.rows(), Core.NORM_MINMAX);
int binWidth = (int) Math.round((double) width / 256);
for (int i = 1; i < 256; i++) {
Imgproc.line(histImage,
new Point(binWidth * (i - 1), height - Math.round(hist.get(i - 1, 0)[0])),
new Point(binWidth * i, height - Math.round(hist.get(i, 0)[0])),
new Scalar(0, 0, 0), 2, 8, 0);
}
return histImage;
}
/**
* 才是直方图
* @param hists
* @param width
* @param height
* @return
*/
private Mat drawColorHistogram(List<Mat> hists, int width, int height) {
Mat histImage = new Mat(height, width, CvType.CV_8UC3, new Scalar(255, 255, 255));
Scalar[] colors = new Scalar[] {
new Scalar(255, 0, 0), // 蓝
new Scalar(0, 255, 0), // 绿
new Scalar(0, 0, 255) // 红
};
int binWidth = (int) Math.round((double) width / 256);
for (int c = 0; c < 3; c++) {
Core.normalize(hists.get(c), hists.get(c), 0, histImage.rows(), Core.NORM_MINMAX);
for (int i = 1; i < 256; i++) {
Imgproc.line(histImage,
new Point(binWidth * (i - 1), height - Math.round(hists.get(c).get(i - 1, 0)[0])),
new Point(binWidth * i, height - Math.round(hists.get(c).get(i, 0)[0])),
colors[c], 2, 8, 0);
}
}
return histImage;
}
}
🔹 代码解析
- 转换灰度图
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
计算灰度图直方图前,需转换为 单通道灰度图。
- 计算灰度直方图
Imgproc.calcHist(images, new int[]{0}, new Mat(), hist, new int[]{256}, new float[]{0, 256});
• new int[]{0} → 计算灰度通道(第 0 通道)。
• new int[]{256} → 设定 256 个 bin(默认灰度级别 0~255)。
• new float[]{0, 256} → 设定计算范围。
- 绘制直方图
Core.normalize(hist, hist, 0, histImage.rows(), Core.NORM_MINMAX);
• Core.normalize() 归一化直方图,以便绘图。
4. 计算彩色直方图(RGB Histogram)
彩色直方图是分别计算 R、G、B 三个通道的直方图。
示例:计算彩色直方图
Mat histR = new Mat(), histG = new Mat(), histB = new Mat();
List<Mat> rgbList = new ArrayList<>();
Core.split(src, rgbList);
Imgproc.calcHist(rgbList, new int[]{0}, new Mat(), histB, new int[]{256}, new float[]{0, 256});
Imgproc.calcHist(rgbList, new int[]{1}, new Mat(), histG, new int[]{256}, new float[]{0, 256});
Imgproc.calcHist(rgbList, new int[]{2}, new Mat(), histR, new int[]{256}, new float[]{0, 256});
🔹 代码解析
- Core.split() 拆分 BGR 三通道:
Core.split(src, rgbList);
• rgbList.get(0) → 蓝色通道 (B)
• rgbList.get(1) → 绿色通道 (G)
• rgbList.get(2) → 红色通道 (R)
- 分别计算直方图
Imgproc.calcHist(rgbList, new int[]{0}, new Mat(), histB, new int[]{256}, new float[]{0, 256});
这里 new int[]{0} 对应 B、G、R 三个通道的索引值。
5. 应用场景
应用 | 用途 |
---|---|
图像增强 | 通过直方图均衡化提高对比度 |
目标检测 | 通过颜色直方图寻找特定颜色区域 |
特征匹配 | 计算直方图相似度进行图像识别 |
曝光检测 | 通过分析亮度直方图判断图像是否曝光过度或过暗 |
6. 总结
✅ calcHist() 计算直方图,适用于灰度图和彩色图
✅ 直方图可用于图像分析、颜色检测、特征匹配
✅ Core.normalize() 归一化可视化直方图
✅ 广泛用于 目标识别、图像增强、模式识别