技术标签: 机器视觉+图像处理+数字信号处理
最近在看一本OpenCV 的书,书名是 《OpenCV 3 Computer Vision Application Programming Cookbook (third edition)》,里面给了很多很实用的代码片段。最近这几篇学习笔记都是从这个书里摘出的代码。有些代码我又做了些小的修改。
直方图计算是个很常见的需求,OpenCV 当然也提供了相应的函数。不过OpenCV 里的函数搞的有点复杂。函数原型如下:
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 );
这个 calcHist 可以同时计算许多个图像的直方图,也支持多个通道图像。通常我们用不到这么复杂的功能。所以可以再进一步封装一下。下面是封装后的代码:
#ifndef HISTOGRAM_H
#define HISTOGRAM_H
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
class Histogram1D
{
public:
Histogram1D()
{
// Prepare arguments for 1D histogram
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 256.0;
ranges[0] = hranges;
channels[0] = 0; // by default, we look at channel 0
}
~Histogram1D();
// Computes the 1D histogram and returns an image of it.
cv::Mat getHistogramImage(const cv::Mat &image);
// Computes the 1D histogram.
cv::MatND getHistogram(const cv::Mat &image);
/**
* @brief stretch 拉伸图像的灰度直方图以增强图像的对比度
* @param image 输入图像,必须是8 bits 灰度图像
* @param percent 直方图两侧各舍弃百分之 percent 的点,将剩下的拉伸到 0 - 255
* @return 返回一个新的图像
*/
cv::Mat stretch(const cv::Mat &image, double percent);
cv::Mat stretch(const cv::Mat &image, double percent1, double percent2);
//直方图正规化,将图像图像最亮的地方线性拉伸到 255,最暗的地方线性拉伸到 0
cv::Mat normalize(const cv::Mat &image);
private:
int histSize[1]; // number of bins
float hranges[2]; // min and max pixel value
const float* ranges[1];
int channels[1]; // only 1 channel used here
};
class ColorHistogram
{
public:
ColorHistogram()
{
// Prepare arguments for a color histogram
histSize[0] = histSize[1] = histSize[2] = 256;
hranges[0] = 0.0; // BRG range
hranges[1] = 256.0;
ranges[0] = hranges; // all channels have the same range
ranges[1] = hranges;
ranges[2] = hranges;
channels[0] = 0; // the three channels
channels[1] = 1;
channels[2] = 2;
}
cv::MatND getHistogram(const cv::Mat &image) ;
cv::SparseMat getSparseHistogram(const cv::Mat &image) ;
private:
int histSize[3];
float hranges[2];
const float* ranges[3];
int channels[3];
};
#endif // HISTOGRAM_H
#include "histogram.h"
#include <QDebug>
#include <random>
Histogram1D::~Histogram1D()
{
}
// Computes the 1D histogram.
cv::MatND Histogram1D::getHistogram(const cv::Mat &image)
{
cv::MatND hist;
// Compute histogram
cv::calcHist(&image,
1, // histogram from 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
1, // it is a 1D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
cv::Mat Histogram1D::stretch(const cv::Mat &image, double percent)
{
return stretch(image, percent, percent);
}
cv::Mat Histogram1D::stretch(const cv::Mat &image, double percent1, double percent2)
{
cv::MatND hist = getHistogram(image);
int imin, imax;
if(percent1 < 0.0) percent1 = 0.0;
if(percent1 > 1.0) percent1 = 1.0;
percent1 = image.rows * image.cols * percent1;
double value = 0;
for(imin = 0; imin < histSize[0]; imin++)
{
value += hist.at<float>(imin);
if(value > percent1) break;
}
value = 0;
if(percent2 < 0.0) percent2 = 0.0;
if(percent2 > 1.0) percent2 = 1.0;
percent2 = image.rows * image.cols * percent2;
for(imax = histSize[0] - 1; imax >= 0; imax--)
{
value += hist.at<float>(imax);
if(value > percent2) break;
}
//int dim = 256;
cv::Mat lookup(1, 256, CV_8U);
for(int i = 0; i < 256; i++)
{
if(i < imin) lookup.at<uchar>(i) = 0;
else if(i > imax) lookup.at<uchar>(i) = 255;
else
{
double v = 255.0 * (i - imin) / (imax - imin);
lookup.at<uchar>(i) = static_cast<uchar>(v);
}
}
cv::Mat ret;
cv::LUT(image, lookup, ret);
return ret;
}
cv::Mat Histogram1D::normalize(const cv::Mat &image)
{
// Compute histogram first
cv::MatND hist = getHistogram(image);
int imin, imax;
for(imin = 0; imin < histSize[0]; imin++)
{
if(hist.at<float>(imin) > 0) break;
}
for(imax = histSize[0] - 1; imax >= 0; imax--)
{
if(hist.at<float>(imax) > 0) break;
}
cv::Mat lookup(1, 256, CV_8U);
for(int i = 0; i < 256; i++)
{
if(i < imin) lookup.at<uchar>(i) = 0;
else if(i > imax) lookup.at<uchar>(i) = 255;
else
{
int v = 255 * (i - imin) / (imax - imin);
lookup.at<uchar>(i) = static_cast<uchar>(v);
}
}
cv::Mat ret;
cv::LUT(image, lookup, ret);
return ret;
}
// Computes the 1D histogram and returns an image of it.
cv::Mat Histogram1D::getHistogramImage(const cv::Mat &image)
{
// Compute histogram first
cv::MatND hist = getHistogram(image);
// Get min and max bin values
double maxVal = 0;
double minVal = 0;
cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
// Image on which to display histogram
cv::Mat histImg(histSize[0], histSize[0], CV_8U, cv::Scalar(255));
// set highest point at 90% of nbins
int hpt = static_cast<int>(0.9 * histSize[0]);
// Draw a 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);
// This function draws a line between 2 points
cv::line(histImg, cv::Point(h, histSize[0]),
cv::Point(h,histSize[0]-intensity), cv::Scalar::all(0));
}
return histImg;
}
cv::MatND ColorHistogram::getHistogram(const cv::Mat &image)
{
cv::MatND hist;
// Compute histogram
cv::calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
cv::SparseMat ColorHistogram::getSparseHistogram(const cv::Mat &image)
{
cv::SparseMat hist(3,histSize,CV_32F);
// Compute histogram
cv::calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
下面给个简单的例子:
cv::Mat image = cv::imread("D:\\向日葵.jpg");
cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
cv::imshow("origin", image);
Histogram1D hist;
cv::Mat h = hist.getHistogramImage(image);
cv::Mat his1 = hist.getHistogram(image);
cv::imshow("hist", h);
image = hist.stretch(image, 0, 0.150);
cv::imshow("2", image);
cv::Mat h2 = hist.getHistogramImage(image);
cv::Mat his2 = hist.getHistogram(image);
cv::imshow("hist2", h2);
这个例子很简单,加载一幅图像,先变成灰度图,然后计算直方图。再对图像的直方图拉伸一下,之后在重新计算直方图。输出结果如下图:
上面的例子是针对灰度图像的,对于彩色图像要用 ColorHistogram 这个类。彩色图像通常具有的颜色非常多,直接计算得到的结果会非常大。通常我们要先对颜色进行缩减操作。这个缩减操作可以用下面这个函数:
void colorReduceIO(const cv::Mat &image, // input image
cv::Mat &result, // output image
int div)
{
int nl = image.rows; // number of lines
int nc = image.cols; // number of columns
int nchannels = image.channels(); // number of channels
// allocate output image if necessary
result.create(image.rows, image.cols, image.type());
for (int j = 0; j < nl; j++)
{
// get the addresses of input and output row j
const uchar* data_in = image.ptr<uchar>(j);
uchar* data_out = result.ptr<uchar>(j);
for (int i = 0; i < nc * nchannels; i++)
{
// process each pixel ---------------------
data_out[i] = data_in[i] / div*div + div / 2;
// end of pixel processing ----------------
} // end of line
}
}
通常我们把图像数据最低2位去掉是不影响显示效果的。去掉最低的 4 位也不影响大多数的后续处理。下面给个简单的例子:
cv::Mat image = cv::imread("D:\\向日葵.jpg");
cv::pyrDown(image, image);
cv::Mat image4, image16, image8;
colorReduceIO(image, image4, 4);
colorReduceIO(image, image8, 8);
colorReduceIO(image, image16, 16);
cv::imshow("origin", image);
cv::imshow("image4", image4);
cv::imshow("image16", image16);
cv::imshow("image8", image8);
结果如下图:
所以缩减一下颜色数量再做颜色直方图会减少大量内存消耗。对后续其他的处理也有帮助。
文章浏览阅读2.8k次。Unity Shader学习:油画效果油画效果在学习浅墨大神的文章时看到的比较有趣,但是原文中也没详细的算法介绍如何实现,这里就先直接拿来用吧,UI和屏幕后处理都可以用,算法也看的不是很明白,好像是ShaderToy上老外搞得。shader部分:Shader "Custom/OilPaintEffect" { Properties{ _MainTex("MainTex",2D)="..._unity 打开摄像头 油画风格
文章浏览阅读91次。冲刺第三天一、Daily Scrum Meeting照片二、每个人的工作1.昨天已完成的任务。昨天完成服务器上的数据库部署与Json数据传输2.今天计划完成的任务。今天计划完成安卓手机登入功能3.工作中遇到的困难。Json传输格式与数据库链接,进行了查询和学习。4.每个人的贡献比。余洋(201421123031):15%..._黄子敬 php
文章浏览阅读93次。SUMMARY:This article provides information on how to manually generate a new system self-signed certificate to replace the expired system self-signed certificate, without resetting the firewall...._the security key has expired, please generate a new key.是什么意思怎么解决
文章浏览阅读2.6w次,点赞18次,收藏51次。# 小白学习之路1.问题描述: 在学习kaggle经典学习项目Titanic,进行数据可视化处理时,对于每个特征进行相关性分析(也就是绘制pearson correlation heatmap )热力相关性矩阵时, plt.show() 图形绘制出来,字体会重叠.导致无法观察# Visualisations"""将数据进行可视化"""print(train.h..._热力图 x轴y轴字体显示
文章浏览阅读929次。无迹卡尔曼滤波UKF算法及源码_无迹卡尔曼滤波
文章浏览阅读527次。Brute force——暴力破解界面:源代码:<?phpif(isset($_GET['Login'])){//Getusername$user=$_GET['username'];//Getpassword$pass=$_GET['password'];$pass=md5($pass);//Checkthedatabase$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";$result=_dvwa brute force一直都是impossible
文章浏览阅读493次。1:天空盒有接缝怎么解决?答:在贴图导入设置里设置Wrap Mode为"Clamp".2:DDS格式怎么不显示?答:Unity不支持DDS格式,Unity会将除DDS外的其他格式图片具有为DDS同样的优化.3:Unity如何动态载入外部模型等文件?答:可以使用AssetBundle:http://unity3d.com/support/documentation/Scr_unity3d 2020.3.7 启动不了
文章浏览阅读452次。第6章 国外著名数据库系统• 6.1 综合性数据库系统• 6.2 专业检索工具6.1 综合性数据库系统• 6.1.1 DIALOG 数据库系统• 6.1.2 OCLC数据库系统6.1.1 DIALOG 数据库系统• 1.系统概述美国DIALOG系统是世界上最早和最大的专业情报检索系统,也是我国科技界广泛使用的系统。 DIALOG(Thomson Corpor..._dialog学术网站
文章浏览阅读263次。If elseif else描述:执行基本的条件流转。参数:名称必需默认类型描述备注test是 boolean决定标志里的内容是否显示的表达式else标志没有这个参数id否
文章浏览阅读1.8k次。文件属性d 开头是: 目录文件。l 开头是: 符号链接(指向另一个文件,类似于瘟下的快捷方式)。s 开头是: 套接字文件(sock)。b 开头是: 块设备文件,二进制文件。c 开头是: 字符设备文件。p 开头是: 命名管道文件。创建套接字文件nc -Ul sock文件权限r = 4w = 2x = 1chmod 660 sock转载于:https://www.cnb..._linux srw
文章浏览阅读3.2k次。映美精 DFK 41BG02.H分辨率 1280X960最大帧率15采用C#编程,使用软件触发模式。触发1次,到ImageAvalible事件发生,记录其时间为96毫秒附近波动。手动计算时间如下: 像素个数:1280 * 960= 1,228,800RGB格式,1个像素3个Byte来表示,其字节数: 1,228,800 * 3 = 3686400相机处理后,读出时间1000 /_c# 映美精相机
文章浏览阅读539次,点赞9次,收藏7次。定义斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=1,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)定义来源于百度百科:斐波那契数列求100以内的斐波那契数列。_python中b组蓝桥杯斐波那契与7代码及思路