关键词
C++ | 数学 | 逻辑 | 按位转换 | 隐写术 | 最低有效位编码策略 | 对比增强 | 线性变换 | 非线性 | 直方图 | 累计 | 直方图均衡 | 概率分布 | 灰度 | 形态学 | 膨胀算子 | 腐蚀算子 | 开运算 | 闭运算 | 滤波器 | 克莱默-布鲁克纳滤波器 | 细化算法 | 骨架化 | 空间过滤 | 离散卷积 | 低通 | 高通 | 一阶导数 | 二阶导数 | Nagao滤波 | 拉普拉斯 | 自适应滤波 | 西格玛滤波 | 自适应窗口 | Deriche 轮廓滤波 | 分贝 | 频域滤波 | 傅里叶 | 莫列波纹 | 线性扩散 | 方程 | 算法 | 视频序列 | 高斯窗口 | 角点检测 | 纹理光谱 | Tamura 系数 | 粗糙度 | 局部二值模式 | 图像分割 | 动作分析 | 多光谱 | 成像捕捉 | 网格 | 变换压缩 | 断层 | 立体视觉 | 径向基函数
🏈指点迷津 | Brief🎯要点
🎯直方图运算:灰度概率分布,累计直方图,直方图均衡。
🎯数学形态学:🖊膨胀和腐蚀算子逻辑数学定义和C++代码,🖊开运算和闭运算逻辑数学定义及C++代码,🖊克莱默-布鲁克纳滤波器逻辑数学定义及C++代码,🖊交替顺序滤波器逻辑数学定义及C++代码,🖊形态梯度数学逻辑定义及C++代码,🖊细化(骨架化)形态学技术逻辑关系及C++代码,🖊细化算法C++代码。
🎯空间过滤:离散卷积逻辑数学定义和C++代码,🖊低通滤波器C++方法,🖊高通滤波器的一阶导数滤波C++方法,🖊二阶导数滤波C++拉普拉斯方法,🖊自适应滤波器C++西格玛滤波方法,🖊自适应窗口滤波器C++ Nagao 滤波方法,C++桑原非利斯托尔滤波方法,🖊Deriche 轮廓滤波器逻辑数学定义和C++代码,🖊Deriche 轮廓滤波器应用于二维图像,🖊计算梯度范数,🖊二阶Deriche 轮廓滤波器对图像拉普拉斯计算,🖊快速傅里叶变换C++代码,应用于两幅图傅里叶变换的模和参数混合,🖊图像光谱的可视化C++代码,以分贝为单位,🖊频域滤波:高斯滤波逻辑数学定义和C++代码,🖊莫列图像的处理,数学关系变换和C++代码去除莫列波纹,🖊线性扩散滤波数学方程和C++代码,🖊非线性扩散滤波C++代码二维上实现 Perona 和 Malik 算法,🖊视频序列上的非线性扩散滤波器C++代码。
🎯特征提取:C++实现高斯窗口,使用 Harris 和 Stephens 算法的实现角点检测,🖊兴趣点的亚像素检测线性系统数学定义和C++实现,🖊霍夫变换:C++对累加器网格进行阈值化,C++直线检测的霍夫变换,🖊圆和椭圆检测:C++计算圆检测的累加器,🖊纹理特征:纹理光谱数学定义及C++实现,🖊Tamura 系数和Tamura对比度C++代码,🖊粗糙度数学定义和C++使用积分图像的局部平均值,🖊C++绝对差异计算,🖊C++Tamura粗糙度计算,🖊纹理的方向性数学定义及C++Tamura的方向计算,🖊局部二值模式和对比度数学定义和C++实现,🖊C++使用局部二值模式串联直方图。
🎯图像分割,🎯二维和三维动作分析,🎯多光谱成像捕捉,🎯可视化和渲染三维网格对象,🎯通过变换压缩,🎯断层重建,🎯立体视觉图像分析,🎯使用径向基函数交互式变形。
🍇C++灰度处理和滤镜函数使用
我们将使用 C++ 和 OpenCV 来读取图像并显示结果。首先,让我们编写一个简单的 C++ 程序来读取来自相机的流并使用 OpenCV 显示 RGB 和灰度图像。
#include <iostream>
#include "opencv2/opencv.hpp"
int main() {
cv::VideoCapture cam(0);
if (!cam.isOpened()) {
throw std::runtime_error("Error");
}
cv::namedWindow("Window");
while (true) {
cv::Mat frame;
cam >> frame;
cv::resize(frame, frame, cv::Size(400, 400));
cv::imshow("bgr_frame", frame);
cv::Mat gray_frame;
cv::cvtColor(frame, gray_frame, CV_BGR2GRAY);
cv::imshow("gray_frame", gray_frame);
if (cv::waitKey(30) >= 0) break;
}
}
我们要在类里安排我们的工作,我们将其称为 ImageOperator。
class ImageOperator{
public:
ImageOperator() = default;
~ImageOperator() = default;
static void to_gray_m1(const cv::Mat& input, cv::Mat& output);
static void to_gray_m2(const cv::Mat& input, cv::Mat& output);
static void to_gray_m3(const cv::Mat& input, cv::Mat& output);
static void to_gray(const unsigned char* input,
const int width,
const int height,
const int channel,
unsigned char* output);
};
您可能注意到,我们在第 6 行到第 9 行中声明了四个函数,用于将 RGB 图像转换为灰度图像。
前三个函数以 OpenCV Mat 矩阵作为输入参数。 我们将探索三种不同的方法将图像转换为灰度。 最后一个函数 to_gray 使用原始 C unsigned char 指针。我们将使用加权方法将 RGB 图像转换为灰度图像。执行此操作的等式是:gray_image = ( (0.3 * R) + (0.59 * G) + (0.11 * B) ).
使用迭代器
它也被称为安全方法。它不如其他方法有效,但它使循环像素变得更加容易。
void ImageOperator::to_gray_m1(const cv::Mat &input, cv::Mat &output) {
unsigned char *data_out = (unsigned char*)(output.data);
int ind = 0;
auto end = input.end<cv::Vec3b>();
cv::MatConstIterator_<cv::Vec3b> it = input.begin<cv::Vec3b>();
for (; it != end; ++it) {
const unsigned char &r = (*it)[2];
const unsigned char &g = (*it)[1];
const unsigned char &b = (*it)[0];
data_out[ind] = 0.3*r+0.59*g+0.11*b;
ind++;
}
}
我们使用 cv::Vec3b 是因为每个元素由三个通道 bgr(蓝色、绿色和红色)组成,每个通道都是 1 字节,因此我们需要 3 字节。 输出 Mat 仅包含一个通道,并且具有与输入 Mat 相同的大小(行数和列数),因此我们可以访问其原始 unsigned char 指针数据并对其进行修改。
使用原始指针和总大小
可以使用 data 属性访问 cv::Mat 的原始数据指针。图像的总字节数可以通过以下公式计算: img_size_in_byte = number_of_channels * img_width*img_height
因此,如果我们有 4040 bgr 图像,则其总大小为 34040 = 4800,相应的灰度图像大小为 140*40 = 1600。这里,我们假设每个通道需要一个字节。
每三个连续字节应转换为灰度图像中的一个值。
void ImageOperator::to_gray_m2(const cv::Mat &input, cv::Mat &output) {
unsigned char *data_in = (unsigned char*)(input.data);
unsigned char *data_out = (unsigned char*)(output.data);
int index = 0;
int byte_size = input.channels()*input.rows*input.cols;
while(index!=byte_size){
data_out[index/input.channels()] = unsigned(0.11*data_in[index]+0.59*data_in[index+1]+0.3*data_in[index+2]);
index+=3;
}
}
在上面的代码片段中,我们使用索引来循环 bgr 输入图像中的字节。我们还将每个循环的索引移动 3,直到它等于总大小(以字节为单位)。
在 for 循环中使用原始指针
图像数据以连续字节的形式存储在内存中。在此方法中,您可以使用其行和列坐标但从 1D 字符指针访问像素。input.step 给出一行中的总字节数。如果将其与行索引相乘,就可以获得指向特定行的指针。访问特定行后,可以使用 col 索引来访问特定列。换句话说:要访问 2D 数组中 x,y 位置的一个像素,可以编写 img[x][y]。要在一维数组中获得相同的结果,必须使用 img[y∗step+x]。
void ImageOperator::to_gray_m3(const cv::Mat &input, cv::Mat &output) {
unsigned char *data_in = (unsigned char*)(input.data);
unsigned char *data_out = (unsigned char*)(output.data);
int index = 0;
for (int row = 0; row < input.rows; ++row) {
for (int col = 0; col < input.cols*input.channels(); col+=input.channels()) {
data_out[index]= 0.11*data_in[row*input.step+col]+
0.59*data_in[row*input.step+col+1]+
0.3*data_in[row*input.step+col+2];
index++;
}
}
}
前面的代码循环遍历 bgr 图像中的每 3 个字节并计算其比例值。
现在,在了解了前面的方法之后,您会发现将 RGB 图像转换为灰度图像非常简单。实际上,甚至不需要解释,因为正如您所注意到的,我们在方法 2 和 3 中使用了原始指针。