在Android上玩转Opencv 系列:16.基础知识 直方图的计算及应用

76 阅读4分钟

Android OpenCV 中,直方图(Histogram) 是一种用于表示图像像素分布的工具,广泛用于 图像分析、对比度增强、特征提取、颜色检测 等任务。

1. 直方图的基本概念

直方图是一个统计图,表示图像中不同灰度值(或颜色值)出现的频率。常见类型:

灰度直方图(Grayscale Histogram):统计 0~255 灰度级 像素的分布。

彩色直方图(Color Histogram):分别统计 RGB 三通道的分布情况

直方图常用于:

  1. 图像对比度分析(判断是否曝光过度、过暗)

  2. 图像增强(直方图均衡化)

  3. 颜色分布分析(颜色识别、目标检测)

  4. 特征匹配(直方图相似度计算)

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;
    }

}

🔹 代码解析

  1. 转换灰度图
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);

计算灰度图直方图前,需转换为 单通道灰度图

  1. 计算灰度直方图
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} → 设定计算范围。

  1. 绘制直方图
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});

🔹 代码解析

  1. Core.split() 拆分 BGR 三通道:
Core.split(src, rgbList);

• rgbList.get(0) → 蓝色通道 (B)

• rgbList.get(1) → 绿色通道 (G)

• rgbList.get(2) → 红色通道 (R)

  1. 分别计算直方图
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() 归一化可视化直方图

广泛用于 目标识别、图像增强、模式识别