opencv学习笔记29-opencv实时显示像素点的灰度值直方图

 一、思路:

(1)主函数 (main):

  • 设置日志级别以隐藏日志信息。
  • 定义是否使用摄像头或视频文件的标志,以及视频文件路径。
  • 创建并打开视频输入,根据标志选择使用摄像头或视频文件。
  • 检查视频是否成功打开,如果失败则打印错误信息并退出。
  • 初始化帧计数器、直方图参数和直方图数组。
  • 循环读取视频帧,对于每一帧,转换为灰度图像,并更新所选像素的直方图。
  • 绘制并显示直方图和当前帧。
  • 等待用户按键,如果用户按下ESC键,则退出循环并结束程序。

(2)鼠标响应函数 (on_mouse):

  • 处理鼠标点击事件,当前只处理鼠标左键按下事件。
  • 更新全局变量 vP 为鼠标点击的坐标。
  • 在鼠标点击的位置上绘制标记,并更新显示图像。

(3)绘制直方图函数 (drawHist):

  • 创建一个用于绘制直方图的空白图像。
  • 初始化直方图图像为白色背景。
  • 计算直方图数组中的最大值,用于归一化。
  • 遍历所有灰度级别,根据直方图数组中的值绘制每个bin的高度。
  • 绘制每个bin的矩形,并填充颜色。

(4)创建视频输入函数 (createInput):

  • 根据传入的标志决定是打开摄像头还是视频文件。
  • 使用 VideoCapture 类创建视频输入,并返回创建的对象。

二、示例代码: 

#include <opencv2/core/utils/logger.hpp>
#include <opencv2/opencv.hpp>           
#include <opencv2/videoio.hpp>       
#include <opencv2/objdetect.hpp>        
#include <opencv2/highgui/highgui_c.h>  
#include <iostream>                     

using namespace cv;                    
using namespace std;      


cv::Point vP;
string wName = "鼠标左键点击选择像素,选择后按任意键开始处理";
VideoCapture createInput(bool useCamera, std::string videoPath);
int drawHist(cv::Mat& histMat, float* srcHist, int bin_width, int bin_heght);
void on_mouse(int EVENT, int x, int y, int flags, void* userdata);

// 主函数入口点
int main() {
    utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);

    // 定义是否使用摄像头或视频文件的标志
    bool useCamera = 0; // 0 表示使用视频文件
    // 定义视频文件的路径
    string videoPath = "C:\\Users\\86173\\Desktop\\TI\\cat.mp4";

    // 创建并打开视频输入,根据 useCamera 标志选择摄像头或视频文件
    VideoCapture capVideo = createInput(useCamera, videoPath);

    // 检查视频是否成功打开
    if (!capVideo.isOpened()) {
        cout << "Unable to open video!" << endl;
        return -1; // 如果无法打开视频,返回错误码
    }

    // 初始化帧计数器
    int cnt = 0;
    // 初始化直方图每个bin的宽度和高度
    int bin_width = 1;
    int bin_heght = 256;
    // 初始化直方图数组,所有值设为0
    float histgram[256] = { 0 };

    // 声明用于显示直方图的Mat对象
    Mat histMat;

    // 循环读取视频帧
    while (1) {
        // 声明帧和灰度图像的Mat对象
        Mat frame;
        Mat grayMat;
        // 读取下一帧
        capVideo >> frame;

        // 如果帧为空,表示视频读取完成
        if (frame.empty()) {
            cout << "视频读取完成!" << endl;
            cout << "视频帧数:" << endl;
            cout << cnt << endl;
            return -1; // 返回错误码
        }
        // 如果是第一帧,复制帧到 selectMat,并设置鼠标回调函数
        if (cnt == 0) {
            Mat selectMat;
            frame.copyTo(selectMat);
            namedWindow(wName); // 创建窗口
            imshow(wName, selectMat); // 显示帧
            setMouseCallback(wName, on_mouse, &selectMat); // 设置鼠标回调
            waitKey(0); // 等待用户点击
            destroyAllWindows(); // 销毁所有窗口
        }
        // 将当前帧从BGR颜色空间转换到灰度空间
        cvtColor(frame, grayMat, COLOR_BGR2GRAY);

        // 如果 vP 未定义,这里需要定义 vP 并初始化
        // 获得鼠标点击选择的像素的灰度值
        int index = grayMat.at<uchar>(vP.y, vP.x);
        // 直方图相应的bin加1
        histgram[index]++;
        // 绘制直方图
        drawHist(histMat, histgram, bin_width, bin_heght);
        // 绘制标记
        drawMarker(frame, vP, Scalar(255, 255, 255));

        // 显示帧和直方图
        imshow("frame", frame);
        imshow("histMat", histMat);

        // 延时30ms以显示图像,等待键盘响应,按下ESC键退出
        if (waitKey(30) == 27) {
            destroyAllWindows(); // 销毁所有窗口
            break; // 退出循环
        }
        cnt++; // 增加帧计数器
    }
    return 0; // 正常退出程序
}
// 鼠标响应函数,用于处理鼠标事件
void on_mouse(int EVENT, int x, int y, int flags, void* userdata) {
	// 声明一个 Mat 对象,用于存储鼠标响应时的用户数据
	Mat zh_x;
	// 从 userdata 中获取 Mat 对象的引用,并赋值给 zh_x
	zh_x = *(Mat*)userdata;

	// 根据鼠标事件类型执行不同的操作
	switch (EVENT) {
		// 鼠标左键按下事件
	case EVENT_LBUTTONDOWN:
	{
		// 更新全局变量 vP.x 和 vP.y 为鼠标点击的坐标
		vP.x = x;
		vP.y = y;
		// 在图像 zh_x 上绘制标记,例如一个圆圈,位置在鼠标点击的坐标
		drawMarker(zh_x, vP, Scalar(255, 255, 255));
		// 注释掉的代码是另一种绘制圆圈的方式,已被 drawMarker 替代
		//circle(zh_x, vP, 4, cvScalar(255, 255, 255), -1);
		// 在窗口 wName 中显示更新后的图像
		imshow(wName, zh_x);
		// 事件处理完毕,退出函数
		return;
	}
	// 结束左键按下事件的处理
	break;
	}
	// 其他事件类型的处理可以继续添加在这里
}

// 请注意,这段代码中有几个问题需要解决:
// 1. 全局变量 vP 需要在使用前声明。
// 2. 变量 wName 需要在使用前声明或定义。
// 3. drawMarker 函数需要在其他地方定义,这

// 绘制直方图的函数
int drawHist(Mat& histMat, float* srcHist, int bin_width, int bin_heght) {
	// 使用 create 函数创建一个用于绘制直方图的空白图像
	// bin_heght 是直方图的高度,256 * bin_width 是直方图的总宽度
	histMat.create(bin_heght, 256 * bin_width, CV_8UC3);

	// 将直方图图像初始化为白色背景
	histMat = Scalar(255, 255, 255);

	// 计算 srcHist 数组中的最大值,用于后续归一化直方图的高
	float maxVal = *std::max_element(srcHist, srcHist + 256);

	// 遍历 0 到 255,代表不同的灰度级
	for (int i = 0; i < 256; i++) {
		// 定义一个矩形,用于绘制直方图的一个 bin
		Rect binRect;
		// 计算 bin 的 x 坐标,每个 bin 的宽度为 bin_width
		binRect.x = i * bin_width;
		// 根据归一化的最大值计算当前 bin 的高度
		float height_i = (float)bin_heght * srcHist[i] / maxVal;
		// 将计算出的高度转换为整数
		binRect.height = (int)height_i;
		// 计算 bin 的 y 坐标,从直方图的底部开始绘制
		binRect.y = bin_heght - binRect.height;
		// 设置 bin 的宽度
		binRect.width = bin_width;
		// 在直方图图像上绘制矩形,使用红色,-1 表示填充整个矩形
		rectangle(histMat, binRect, CV_RGB(255, 0, 0), -1);
	}

	// 函数执行成功,返回 0
	return 0;
}
//选择视频或者摄像头,1摄像头,0视频。
VideoCapture createInput(bool useCamera, string videoPath)
{
	//选择输入
	VideoCapture capVideo;
	if (useCamera) {
		capVideo.open(0);
	}
	else {
		capVideo.open(videoPath);
	}
	return capVideo;
}

三、运行结果:

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是在Qt中使用OpenCV获取像素点灰度分布并绘制直方图的示例代码: ```c++ #include <opencv2/opencv.hpp> #include <QImage> #include <QPixmap> #include <QLabel> #include <QVBoxLayout> #include <QHBoxLayout> #include <QFileDialog> #include <QDebug> using namespace cv; int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget widget; QVBoxLayout *layout = new QVBoxLayout(&widget); // 选择图片 QString fileName = QFileDialog::getOpenFileName(&widget, "Open Image", ".", "Image Files (*.png *.jpg *.bmp)"); if (fileName.isNull()) { return 0; } // 读取图片并转换为灰度图 Mat image = imread(fileName.toStdString()); cvtColor(image, image, CV_BGR2GRAY); // 计算像素点灰度分布 int histSize = 256; float range[] = { 0, 256 }; const float* histRange = { range }; bool uniform = true; bool accumulate = false; Mat hist; calcHist(&image, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate); // 绘制直方图 int histWidth = 512; int histHeight = 400; int binWidth = cvRound((double)histWidth / histSize); Mat histImage(histHeight, histWidth, CV_8UC1, Scalar(255, 255, 255)); normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); for (int i = 1; i < histSize; i++) { line(histImage, Point(binWidth * (i - 1), histHeight - cvRound(hist.at<float>(i - 1))), Point(binWidth * i, histHeight - cvRound(hist.at<float>(i))), Scalar(0, 0, 0), 2, LINE_AA); } // 将结果显示在界面上 QLabel *imageLabel = new QLabel(&widget); QImage qImage(image.data, image.cols, image.rows, QImage::Format_Grayscale8); QPixmap pixmap = QPixmap::fromImage(qImage); imageLabel->setPixmap(pixmap); QLabel *histLabel = new QLabel(&widget); QImage qHistImage(histImage.data, histImage.cols, histImage.rows, QImage::Format_Grayscale8); QPixmap histPixmap = QPixmap::fromImage(qHistImage); histLabel->setPixmap(histPixmap); QHBoxLayout *hLayout = new QHBoxLayout(); hLayout->addWidget(imageLabel); hLayout->addWidget(histLabel); layout->addLayout(hLayout); widget.show(); return a.exec(); } ``` 该代码首先通过 `cv::imread()` 函数读取指定的图片,并将其转换为灰度图。然后使用 `cv::calcHist()` 函数计算像素点灰度分布。 接下来,代码创建一个 `cv::Mat` 对象来绘制直方图。在这个对象中,每个像素代表一个灰度值的计数。通过 `cv::normalize()` 函数将这些计数转换为像素点的数量,以便在绘制直方图时进行比例缩放。最后,使用 `cv::line()` 函数在 `cv::Mat` 对象上绘制直方图。 最后,代码将原始图片和直方图分别显示在 `QLabel` 控件中,然后将这些控件放置在 `QHBoxLayout` 容器中,并将该容器添加到 `QVBoxLayout` 容器中。最终,通过调用 `QWidget::show()` 函数显示主窗口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值