【机器视觉学习笔记】直方图的绘制及直方图均衡化(C++)

目录

  • 概念
    • 直方图定义
    • 直方图均衡化
      • 为什么要选用累积分布函数
      • 如何运用累积分布函数使得直方图均衡化
  • C++ 源码
    • 直方图均衡化
    • 绘制直方图
    • 主函数
  • 效果
  • 完整源码

平台:Windows 10 20H2
Visual Studio 2015
OpenCV 4.5.3


本文综合自直方图计算和程序员-图哥——图像处理之直方图均衡化及C++实现

概念

直方图定义

在这里插入图片描述

直方图均衡化

在这里插入图片描述
直方图均衡化的作用是图像增强。

有两个问题比较难懂,一是为什么要选用累积分布函数,二是为什么使用累积分布函数处理后像素值会均匀分布。

为什么要选用累积分布函数

均衡化过程中,必须要保证两个条件:
①像素无论怎么映射,一定要保证原来的大小关系不变,较亮的区域,依旧是较亮的,较暗依旧暗,只是对比度增大,绝对不能明暗颠倒;
②如果是八位图像,那么像素映射函数的值域应在0和255之间的,不能越界。

综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(控制大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。

如何运用累积分布函数使得直方图均衡化

直方图均衡化过程中,映射方法是
在这里插入图片描述
其中,n是图像中像素的总和,nkn_knk是当前灰度级的像素个数,L是图像中可能的灰度级总数。

C++ 源码

直方图均衡化

直方图均衡化的代码实现有以下几个步骤:

遍历全图,先统计每个灰度级下的像素点个数(为此我们开辟了256大小的数组);
计算每个灰度级的像素点占总像素的点的比例;
按照第二步求出的比例重新计算每个灰度级下的新的灰度值,即均衡化;
依照新的灰度值表遍历更新图像的灰度值。

Mat getEqualizeHistImage(Mat input)
{int gray[256] = { 0 };  //记录每个灰度级别下的像素个数double gray_prob[256] = { 0 };  //记录灰度分布密度double gray_distribution[256] = { 0 };  //记录累计密度int gray_equal[256] = { 0 };  //均衡化后的灰度值int gray_sum = 0;  //像素总数Mat chRGB[3];split(input, chRGB);Mat output = input.clone();for (unsigned char k = 0; k < 3; ++k){for (unsigned short i = 0; i < 256; ++i){gray[i] = 0;gray_prob[i] = 0;gray_distribution[i] = 0;gray_equal[i] = 0;}gray_sum = chRGB[k].cols * chRGB[k].rows;//统计每个灰度下的像素个数for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){int vaule = p[j];gray[vaule]++;}}//统计灰度频率for (int i = 0; i < 256; i++){gray_prob[i] = ((double)gray[i] / gray_sum);}//计算累计密度gray_distribution[0] = gray_prob[0];for (int i = 1; i < 256; i++){gray_distribution[i] = gray_distribution[i - 1] + gray_prob[i];}//重新计算均衡化后的灰度值,四舍五入。参考公式:(N-1)*T+0.5for (int i = 0; i < 256; i++){gray_equal[i] = (uchar)(255 * gray_distribution[i] + 0.5);}//直方图均衡化,更新原图每个点的像素值for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){p[j] = gray_equal[p[j]];}}}merge(chRGB, 3, output);return output;
}

绘制直方图

Mat getHistImage(Mat input)
{Mat chRGB[3];split(input, chRGB);// 设定bin数目int histSize = 255;// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true; bool accumulate = false;Mat RGB_Hist[3];for(uint8_t i = 0; i < 3; ++i)// 计算直方图:calcHist(&chRGB[i], 1, 0, Mat(), RGB_Hist[i], 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));for(uint8_t i = 0; i < 3; ++i)// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(RGB_Hist[i], RGB_Hist[i], 0, histImage.rows, NORM_MINMAX, -1, Mat());// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[0].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[0].at<float>(i))),Scalar(0, 0, 255), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[1].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[1].at<float>(i))),Scalar(0, 255, 0), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[2].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[2].at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);}/// 显示直方图///imshow("calcHist Demo", histImage);return histImage;
}

主函数

图片路径根据实际情况调整,注意反斜杠是转义字符的开头,故“\”应替换为“\\”

int main(int argc, char * argv[])
{Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg");imshow("原图", Image);imshow("原图直方图", getHistImage(Image));imshow("直方图均衡化后的图像", getEqualizeHistImage(Image));imshow("直方图均衡化后的直方图", getHistImage(getEqualizeHistImage(Image)));waitKey(0);return 0;
}

效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完整源码

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;Mat getHistImage(Mat input)
{Mat chRGB[3];split(input, chRGB);// 设定bin数目int histSize = 255;// 设定取值范围 ( R,G,B) )float range[] = { 0, 255 };const float* histRange = { range };bool uniform = true; bool accumulate = false;Mat RGB_Hist[3];for(uint8_t i = 0; i < 3; ++i)// 计算直方图:calcHist(&chRGB[i], 1, 0, Mat(), RGB_Hist[i], 1, &histSize, &histRange, uniform, accumulate);// 创建直方图画布int hist_w = 400; int hist_h = 400;int bin_w = cvRound((double)hist_w / histSize);Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));for(uint8_t i = 0; i < 3; ++i)// 将直方图归一化到范围 [ 0, histImage.rows ]normalize(RGB_Hist[i], RGB_Hist[i], 0, histImage.rows, NORM_MINMAX, -1, Mat());// 在直方图画布上画出直方图for (int i = 1; i < histSize; i++){line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[0].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[0].at<float>(i))),Scalar(0, 0, 255), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[1].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[1].at<float>(i))),Scalar(0, 255, 0), 2, 8, 0);line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(RGB_Hist[2].at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(RGB_Hist[2].at<float>(i))),Scalar(255, 0, 0), 2, 8, 0);}/// 显示直方图///imshow("calcHist Demo", histImage);return histImage;
}Mat getEqualizeHistImage(Mat input)
{int gray[256] = { 0 };  //记录每个灰度级别下的像素个数double gray_prob[256] = { 0 };  //记录灰度分布密度double gray_distribution[256] = { 0 };  //记录累计密度int gray_equal[256] = { 0 };  //均衡化后的灰度值int gray_sum = 0;  //像素总数Mat chRGB[3];split(input, chRGB);Mat output = input.clone();for (unsigned char k = 0; k < 3; ++k){for (unsigned short i = 0; i < 256; ++i){gray[i] = 0;gray_prob[i] = 0;gray_distribution[i] = 0;gray_equal[i] = 0;}gray_sum = chRGB[k].cols * chRGB[k].rows;//统计每个灰度下的像素个数for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){int vaule = p[j];gray[vaule]++;}}//统计灰度频率for (int i = 0; i < 256; i++){gray_prob[i] = ((double)gray[i] / gray_sum);}//计算累计密度gray_distribution[0] = gray_prob[0];for (int i = 1; i < 256; i++){gray_distribution[i] = gray_distribution[i - 1] + gray_prob[i];}//重新计算均衡化后的灰度值,四舍五入。参考公式:(N-1)*T+0.5for (int i = 0; i < 256; i++){gray_equal[i] = (uchar)(255 * gray_distribution[i] + 0.5);}//直方图均衡化,更新原图每个点的像素值for (int i = 0; i < chRGB[k].rows; i++){uchar* p = chRGB[k].ptr<uchar>(i);for (int j = 0; j < chRGB[k].cols; j++){p[j] = gray_equal[p[j]];}}}merge(chRGB, 3, output);return output;
}int main(int argc, char * argv[])
{Mat Image = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\1.jpg");imshow("原图", Image);imshow("原图直方图", getHistImage(Image));imshow("直方图均衡化后的图像", getEqualizeHistImage(Image));imshow("直方图均衡化后的直方图", getHistImage(getEqualizeHistImage(Image)));waitKey(0);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/400674.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C/C++ 如何劫持别人家的命令||函数||程序(只能对于window而言)

要实现下面程序&#xff0c;首先我们需要三个文件 detours.h &#xff0c;detours.lib &#xff0c;detver.h&#xff08;可以去网上下载&#xff09; 1. 首先让我们看看&#xff0c;一个最简单的C程序&#xff0c;如何劫持system函数. 1 #include<stdio.h>2 #include<…

【机器视觉学习笔记】伽马变换(C++)

目录概念C源码变换函数主函数效果完整源码平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文内容节选自《数字图像处理》第三版 C源码修改自C数字图像处理&#xff08;1&#xff09;-伽马变换 —— 图像大师 概念 C源码 变换函数 //函数名&#xff1…

Linux文件查找之find秘笈

前言Linux的基本特点之一是一切皆文件&#xff0c;在系统管理过程中难免会需要查找特定类型的文件&#xff0c;那么问题来了&#xff1a;如何进行有效且准确的查找呢&#xff1f;本文将对Linux系统中的文件查找工具及用法进行详细讲解。常用工具对比常用的文件查找工具主要有lo…

【机器视觉学习笔记】大津法/Otsu最大类间方差法 最佳阈值处理(C++)

目录概念C源码OtsuThreshold主函数效果完整源码平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文所用源码修改自C opencv 图片二值化最佳阈值确定&#xff08;大津法,OTSU算法)——Sharon Liu 概念 Otsu算法&#xff0c;也叫最大类间方差法&#xff0…

HTML 页面源代码布局介绍

此介绍以google首页源代码截图为例&#xff1a; 从上到下依次介绍&#xff1a; 1.<!DOCTYPE html> 此标签可告知浏览器文档使用哪种 HTML 或 XHTML 规范。 XHTML规范&#xff1a;必须小写&#xff0c;有开始结束标签&#xff0c;属性也用双引号。 HTML规范&#xff1a;不…

Python对Protobuf进行序列化与反序列化

Python Protobuf1.了解Protobuf&#xff1a;1.1 Protobuf语法介绍&#xff1a;2. Python使用Protobuf&#xff1a;(windows平台上)1.了解Protobuf&#xff1a; 我们在使用protobuf之前首先要了解protobuf&#xff0c;那么什么是protobuf呢&#xff1f; 官方的解释是&#xff…

sql server management studio 查询的临时文件路径

C:\Users\你的登录名称\Documents\SQL Server Management Studio\Backup FilesC:\Users\你的登录名称\AppData\Local\Temp\sql server management studio 非正常关闭时自动保存的路径在sql server management studio 里创建的“新建查询”且没有手动保存的路径转载于:https://w…

K210 / Openmv实现 大津法/Otsu最大类间方差法 自适应二值化

目录源码效果平台&#xff1a;K210 固件版本&#xff1a;maixpy_v0.6.2_54_g897214100_openmv_kmodel_v4_with_ide_support.bin OpenMv库自带Otsu算法: 源码 # Otsu.py - By: Royic - 周三 9月 22 2021import sensor, imagesensor.reset() sensor.set_pixformat(sensor.GRAY…

第一章:OpenCV入门

第一章&#xff1a;OpenCV入门 OpenCV是一个开源的计算机视觉库&#xff0c;1999年有英特尔的Gary Bradski启动。OpenCV库由C和C语言编写&#xff0c;涵盖计算机视觉各个领域内的500多个函数&#xff0c;可以在多个操作系统上运行。它旨在提供一个简洁而又高效的接口&#xff…

【机器视觉学习笔记】双边滤波算法(C++)

目录源码滤波器主函数效果完整源码平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文所用源码修改自双边滤波(bilateral filter)以及联合双边滤波&#xff08;joint bilateral filter&#xff09;—— flow_specter 源码 滤波器 // 双边滤波 // src…

第二章:图像处理基础

第二章&#xff1a;图像处理基础操作一、图像的基本表示方法&#xff1a;1. 二值图像&#xff1a;2. 灰度图像&#xff1a;3. 彩色图像&#xff1a;二、像素处理&#xff1a;1. 二值图像及灰度图像&#xff1a;2.彩色图像&#xff1a;3. 使用numpy.array访问像素&#xff1a;三…

《Head First设计模式》 读书笔记16 其余的模式(二) 蝇量 解释器 中介者

《Head First设计模式》 读书笔记16 其余的模式&#xff08;二&#xff09; 蝇量 解释器 中介者 蝇量&#xff08;Flyweight Pattern&#xff09; 如想让某个类的一个实例能用来提供许多“虚拟实例”&#xff0c;就使用蝇量模式&#xff08;Flyweight Pattern&#xff09; 。 例…

洛谷P1525 关押罪犯

P1525 关押罪犯 题目描述 S 城现有两座监狱&#xff0c;一共关押着N 名罪犯&#xff0c;编号分别为1~N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久&#xff0c;如果客观条件具备则随时可能爆发冲突。我们用“怨气值”&#xff08;一个正整数值&#xff09;来表示…

【机器视觉学习笔记】Hough变换直线检测(C++)

目录源码效果平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文源码摘自OpenCV2马拉松第22圈——Hough变换直线检测原理与实现 源码 #include <opencv2\opencv.hpp> #include <iostream> #include <opencv2\imgproc\types_c.h> #in…

第3章:图像运算

第3章&#xff1a;图像运算one. 图像加法运算&#xff1a;1. 加号运算符"":2. cv2.add()函数&#xff1a;two. 图像加权和&#xff1a;three. 按位逻辑运算&#xff1a;1. 按位与运算&#xff1a;2. 按位或运算&#xff1a;3.按位非运算&#xff1a;4. 按位异或运算&…

JMS中的消息通信模型

1. MQ简介&#xff1a; 消息队列&#xff08;Message Queue&#xff0c;简称MQ&#xff09;,是应用程序与应用程序之间的一种通信方法。应用程序通过发送和检索出入列队的针对应用程序的数据 - 消息来通信&#xff0c;而无需专用连接来链接它们。程序之间通过在消息中发送数据进…

【机器视觉学习笔记】最近邻插值实现图片任意角度旋转(C++)

目录原理源码RotateImage主函数效果完整源码速度优化源码优化效果平台&#xff1a;Windows 10 20H2 Visual Studio 2015 OpenCV 4.5.3 本文算法改进自图形算法与实战&#xff1a;6.图像运动专题&#xff08;5&#xff09;图像旋转-基于近邻插值的图像旋转 —— 进击的CV 原理…

UGUI的优点新UI系统

UGUI的优点新UI系统 第1章 新UI系统概述 UGUI的优点新UI系统&#xff0c;新的UI系统相较于旧的UI系统而言&#xff0c;是一个巨大的飞跃&#xff01;有过旧UI系统使用体验的开发者&#xff0c;大部分都对它没有任何好感&#xff0c;以至于在过去的很长一段时间里&#xff0c;大…

【探索HTML5第二弹05】响应式布局(中),一步一步响应式布局

前言 前天初步探究了一次响应式布局&#xff0c;虽然花了一天功夫&#xff0c;做出来的东西还是不行&#xff0c;在此我还是认为要做响应式布局设计应该先行&#xff0c;应该先制作3个以上的设计图出来&#xff0c;但是对于手机来说&#xff0c;图片流量也是个问题&#xff0c;…

通过使用CSS字体阴影效果解决hover图片时显示文字看不清的问题

1.前言 最近需要加入一个小功能&#xff0c;在鼠标越过图片时&#xff0c;提示其大小和分辨率&#xff0c;而不想用增加属性title来提醒&#xff0c;不够好看。然而发现如果文字是一种颜色&#xff0c;然后总有概率碰到那张图上浮一层的文字会看不到&#xff0c;所以加入文字字…