opencv学习笔记第四章 计算图像的直方图

本文介绍了使用OpenCV进行图像直方图计算、获取最大最小值及位置、绘制线条及图像二值化的方法。通过具体代码示例展示了如何实现这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直方图:图像像素值的统计表。灰度图像有256个条目(容器)

获取直方图:

void calcHist( const Mat* images, int nimages, const int* channels, InputArray mask,
OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform = true, bool accumulate = false );

获取数组的最大值和最小值:

void cvMinMaxLoc( const CvArr* arr, double* min_val, double* max_val,
CvPoint* min_loc=NULL, CvPoint* max_loc=NULL, const CvArr* mask=NULL );
arr
输入数组, 单通道或者设置了 COI 的多通道。
min_val
指向返回的最小值的指针。
max_val
指向返回的最大值的指针。
min_loc
指向返回的最小值的位置指针。
max_loc
指向返回的最大值的位置指针。mask
选择一个子数组的操作掩模。

画线函数:

void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
                     int thickness = 1, int lineType = LINE_8, int shift = 0);

二值化函数:

void cvThreshold( const CvArr* src, CvArr* dst, double threshold,
double max_value, int threshold_type );
src 原始数组 (单通道, 8-比特 of 32-比特 浮点数).
dst 输出数组,必须与 src 的类型一致,或者为 8-比特.
threshold 阈值
max_value 使用 CV_THRESH_BINARY CV_THRESH_BINARY_INV 的最大值.
threshold_type 阈值类型 (见讨论)
函数 cvThreshold 对单通道数组应用固定阈值操作。该函数的典型应用是对灰度图像
进行阈值操作得到二值图像。(
cvCmpS 也可以达到此目的) 或者是去掉噪声,例如过滤
很小或很大象素值的图像点。本函数支持的对图像取阈值的方法由 threshold_type 确
定:
threshold_type=CV_THRESH_BINARY:
dst(x,y) = max_value, if src(x,y)>threshold

0, otherwise
threshold_type=CV_THRESH_BINARY_INV:
dst(x,y) = 0, if src(x,y)>threshold
max_value, otherwise
threshold_type=CV_THRESH_TRUNC:
dst(x,y) = threshold, if src(x,y)>threshold
src(x,y), otherwise
threshold_type=CV_THRESH_TOZERO:
dst(x,y) = src(x,y), if (x,y)>threshold
0, otherwise
threshold_type=CV_THRESH_TOZERO_INV:
dst(x,y) = 0, if src(x,y)>threshold
src(x,y), otherwise


#include <stdio.h>
#include <iostream>
#include "opencv2/opencv.hpp"
#include "histogram.h"
using namespace std;
using namespace cv;

int main()
{
	Histogram1D h;
	Mat image;
	image = imread("D:/1.jpg", 0);
	/*
	MatND histo = h.getHistogram(image);
	for (int i = 0; i < 256;i++)
		//cout << "Value" << i << "=" << hist.at<float>(i) << endl;
	*/
	/*namedWindow("Histogram");
	imshow("Histogram", h.getHistogramImage(image));
	*/
	/********************************************************
	二值化
	********************************************************/
	Mat thresholded;
	threshold(image, thresholded, 60, 255, THRESH_BINARY);
	namedWindow("thresholded");
	imshow("thresholded", thresholded);
	
	waitKey(0);
}
#pragma once
#ifndef HISTOGRAM_H
#define HISTOGRAM_H

#include "opencv2/opencv.hpp"
using namespace cv;

class Histogram1D
{
private:
	int histSize[1];			//项的数量
	float hranges[2];			//像素的最小以及最大值
	const float *ranges[1];		//
	int channels[1];			//通道
public:
	Histogram1D()				//构造函数
	{
		histSize[0] = 256;
		hranges[0] = 0.0;
		hranges[1] = 255.0;
		ranges[0] = hranges;
		channels[0] = 0;		//默认考察0通道
	}
	MatND getHistogram(const Mat &image)
	{
		MatND hist;				//hist 是一个一维数组

		calcHist(&image, 1, channels, Mat(), hist, 1, histSize, ranges);
		/*
		CV_EXPORTS void calcHist( const Mat* images, int nimages, const int* channels, InputArray mask,
		OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform = true, bool accumulate = false );

		CV_EXPORTS void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask,
			SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform = true, bool accumulate = false);

		CV_EXPORTS_W void calcHist(InputArrayOfArrays images, const std::vector<int>& channels, InputArray mask, OutputArray hist,
			const std::vector<int>& histSize, const std::vector<float>& ranges, bool accumulate = false);
		image。输入的图像的指针,可以是多幅图像,所有的图像必须有同样的深度(CV_8U or CV_32F)。同时一副图像可以有多个channes。
        narrays。输入的图像的个数。
        channels。用来计算直方图的channes的数组。比如输入是2副图像,第一副图像有0,1,2共三个channel,第二幅图像只有0一个channel,
那么输入就一共有4个channes,如果int channels[3] = {3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计
算直方图。
        mask。掩码。如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和arrays[i]的大小相同,值为1的点将用来计算
直方图。
        hist。计算出来的直方图
        dims。计算出来的直方图的维数。
        histSize。在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个数。
        ranges。用来进行统计的范围。比如
        float rang1[] = {0, 20};
        float rang2[] = {30, 40};
        const float *rangs[] = {rang1, rang2};那么就是对0,20和30,40范围的值进行统计。
		uniform。每一个竖条的宽度是否相等
		*/
		return hist;
	}
	Mat getHistogramImage(const Mat &image) 
	{
		// Compute histogram first
		MatND hist = getHistogram(image);

		// Get min and max bin values
		double maxVal = 0;
		double minVal = 0;
		minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		// Image on which to display histogram
		Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));

		// set highest point at 90% of nbins
		int hpt = static_cast<int>(0.9*histSize[0]);

		// Draw vertical line for each bin 
		for (int h = 0; h < histSize[0]; h++) {

			float binVal = hist.at<float>(h);
			int intensity = static_cast<int>(binVal*hpt / maxVal);
			line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));
			/*
			void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
                     int thickness = 1, int lineType = LINE_8, int shift = 0);
			*/
		}

		return histImg;
	}
};

#endif // !HISTOGRAM_H

















第一课:绘制直方图 import cv2 as cv import numpy as np from matplotlib import pyplot as plt def plot_demo(image): plt.hist(image.ravel(),256,[0,256]) plt.show() def image_hist(image): color = ('blue','green','red') for i,color in enumerate(color): #计算直方图 hist = cv.calcHist(image,[i],None,[256],[0,256]) #画出直方图 plt.plot(hist,color=color) plt.xlim(0,256) plt.show() src = cv.imread("E:/opencv/picture/Greatwall.jpg") cv.imshow("initial_window",src) plot_demo(src) image_hist(src) cv.waitKey(0) cv.destroyAllWindows() 效果图: 7-1源图像 7-2 plot_demo 7-3 image_hist 分析: 1. 什么叫直方图? 答:简单来说就是图像中每个像素值的个数统计。比如说一副灰度图中像素值为0的有多少个,像素值为1的有多少个….直方图是一种分析图片的手段。 在计算直方图之前,有几个术语需要先了解一下: dims:要计算的通道数,对于灰度图dims = 1,对于rgb图像dims= 3 range:要计算的像素值范围,一般为【0,256】(不包括256) bins:子区间数目,如果我们统计0~255每个像素值,bins =256;如果划分区间,比如【0,15】,【16,31】,….【240,255】这样的16个区间,bins = 16 2. 需要安装matplotlib pip install matplotlib 3. plot_demo(matplotlib自带的计算并绘制直方图) def plot_demo(image): plt.hist(image.ravel(),256,[0,256]) plt.show() 1) image.ravel()函数的功能是将多维数组降为一维数组 2)matplotlib.pyplot.hist函数主要是计算直方图 hist函数原型:hist(x, bins=None, range=None,….) x参数表示是一个数组或一个序列,是指定每个bin(箱子)分布的数据 bins参数表示指定bin(箱子)的个数,也就是总共有几条条状图 range参数表示箱子的下限和上限。即横坐标显示的范围,范围之外的将被舍弃。 range这个参数一般都是[0,256] 3)plt.show()显示直方图 4. image_hist def image_hist(image): #注意这个是tuple()哦,且里面的是字符‘’而不是字符串 color = ('blue','green','red') for i,color in enumerate(color): hist =cv.calcHist(image,[i],None,[256],[0,256]) plt.plot(hist,color=color) plt.xlim(0,256) plt.show() 1) enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据下标和数据,一般用在 for 循环当中。 语法: enumerate(sequence, [start=0]) 参数: • sequence -- 一个序列、迭代器或其他支持迭代对象。 • start -- 下标起始位置。 返回值 返回 enumerate(枚举) 对象。 举个例子: 对于普通的for循环: >>>i = 0 >>> seq = ['one', 'two', 'three'] >>> for element in seq: print i, seq[i] i +=1 0 one 1 two 2 three 对于for循环使用enumerate: >>>seq = ['one', 'two', 'three'] >>> for i, element in enumerate(seq): print i, element 0 one 1 two 2 three 2)cv2.calcHist的原型为:calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]]) -> hist images参数表示输入图像,传入时应该用中括号[ ]括起来 channels参数表示传入图像的通道,如果是灰度图像,那就不用说了,只有一个通道,值为0,如果是彩色图像(有3个通道),那么值为0,1,2,中选择一个,对应着BGR各个通道。这个值也得用[ ]传入。 mask参数表示掩膜图像。如果统计整幅图,那么为None。主要是如果要统计部分图的直方图,就得构造相应的掩膜来计算。 histSize参数表示灰度级的个数,需要中括号,比如[256] ranges参数表示像素值的范围,通常[0,256]。此外,假如channels为[0,1],ranges为[0,256,0,180],则代表0通道范围是0-256,1通道范围0-180。 3)plt.plot与plt.xlim函数 plt.plot(hist,color=color) plt.xlim(0,256) plt.plot函数:绘制函数图像 参数一:列表或者数组 参数二:控制颜色参数 plt.xlim函数:设置坐标轴刻度的取值范围 第二课 直方图均衡化与比较 均衡化(全局与部分) opencv直方图均衡化都是基于灰度图像的 作用:直方图均衡化可以调整图像的对比度,使图像更加清晰,对比度增强。是图像增强的一个手段。 直方图均衡化:如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是一种能仅靠输入图像直方图信息自动达到这种效果的变换函数。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像元取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。 源码: import cv2 as cv import numpy as np from matplotlib import pyplot as plt def hist_demo(image): hist = plt.hist(image.ravel(),256,[0,256]) plt.show() def Image_hist(image): color = ("b","g","r") for i,color in enumerate(color): #计算直方图 hist = cv.calcHist(image,[i],None,[256],[0,256]) plt.xlim(0,256) plt.plot(hist,color) plt.show() #opencv直方图均衡化都是基于灰度图像的 #直方图均衡化可以自动的调整图像的对比度,使图像更加清晰化,对比度增强,是图像增强的一个手段 def equalHist_demo(image): gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) cv.imshow("gray",gray) dst = cv.equalizeHist(gray) cv.imshow("dst_win1",dst) #可以明显的看出dst_win1的对比度要比gray的对比度要高好多。 def clahe_demo(image): gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) cv.imshow("gray", gray) clahe = cv.createCLAHE(clipLimit=5.0,tileGridSize=(8,8)) dst = clahe.apply(gray) cv.imshow("dst_win2", dst) #之前学习的调节对比度以及锐化图像的方法 def contrast_demo(image,c,gamm): #blank = np.zeros(image.shape,np.uint8) #dst = cv.addWeighted(image,c,blank,0,gamm) kernel = np.array([[0,-1,0],[-1,5,-1],[0,-1,0]]) dst = cv.filter2D(image,-1,kernel) cv.imshow("dst_win",dst) src = cv.imread("E:/opencv/picture/lena.jpg") cv.imshow("inital_win",src) #hist_demo(src) #Image_hist(src) #contrast_demo(src,2,2) equalHist_demo(src) cv.waitKey(0) cv.destroyAllWindows() 得出结果如下: 原图 灰度图 图像全均衡化 自适应直方图均衡化(均衡参数可调推荐) 分析: 1)#opencv直方图均衡化都是基于灰度图像的 #直方图均衡化可以自动的调整图像的对比度,使图像更加清晰化,对比度增强,是图像增强的一个手段 #全局直方图均衡化 def equalHist_demo(image): gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY) cv.imshow("gray",gray) dst = cv.equalizeHist(gray) cv.imshow("dst_win1",dst) #可以明显的看出dst_win1的对比度要比gray的对比度要高好多。 api: cv.equalizeHist(src) 对输入的源灰度图像进行直方图均衡化处理,从而提高图像质量。如果源图像是彩色图像,得需先转化成灰度图像。 2) 自适应(局部)直方图均衡化(可调参数) .全局直方图均衡化可能得到是一种全局意义上的均衡化,但是有的时候这种操作并不是很好,会把某些不该调整的部分给调整了。Opencv中还有一种直方图均衡化,它是一种局部直方图均衡化,也就是是说把整个图像分成许多小块(比如按10*10作为一个小块),那么对每个小块进行均衡化。 注:全图的直方图均衡化会导致对比度过度增强,所以在一些情况下应使用局部直方图均衡化; #局部直方图均衡化 def clahe_demo(image): gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) cv.imshow("gray", gray) clahe = cv
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值