opencv(C++)处理图像颜色
文章目录
- 介绍
- 使用策略设计模式比较颜色
- 实现方案
- 计算两个颜色向量之间的距离
- 1. 简单方法:曼哈顿距离计算(Manhattan Distance)
- 2.使用 OpenCV 的 cv::norm 函数
- 3.使用 OpenCV 的 cv::absdiff 函数
- 错误示例
- 使用 OpenCV 函数实现颜色检测
- 实现方案
- 阈值处理:cv::threshold
- cv::THRESH_BINARY_INV 模式:
- 其他有用的模式
- 使用 cv::floodFill 函数
- cv::floodFill 的工作原理
- 实现方案
- 应用场景
- 使用函数对象
- 实现方案
- 适用场景
- OpenCV 的算法基类:cv::Algorithm
- cv::Algorithm 的核心功能
- cv::Algorithm 的多态性
- 优点
- 适用场景
- 使用GrabCut算法分割图像
- 图像分割实现步骤
- 实现方案
- 转换颜色
- RGB 颜色空间
- RGB 的局限性
- CIE Lab* 颜色空间
- 颜色转换的步骤
- 其他常用颜色空间转换
- 工作原理
- 用色调、饱和度和亮度表示颜色
- 颜色空间转换及可视化HSV各分量
- HSV颜色空间简介
- 色调 (Hue)
- 饱和度 (Saturation)
- 亮度 (Value)
- OpenCV 中的 HSV 实现
- 生成 HSV 色彩表
- 修改图像亮度
- 将BGR图像转换为HSV颜色空间
- 从HSV转回BGR颜色空间
- 分离并可视化HSV各通道
- 实现案例
- 使用颜色进行肤色检测
- 案例实现
介绍
- 使用策略设计模式比较颜色
- 使用GrabCut算法分割图像
- 转换颜色表示法
- 使用色调、饱和度和亮度表示颜色
使用策略设计模式比较颜色
假设需要构建一个简单的算法,用于识别图像中所有具有指定颜色的像素。为实现这一目标,该算法需要接受一张图像和一种颜色作为输入,并返回一张二值图像,显示哪些像素具有指定的颜色。此外,还需要在运行算法之前指定一个容差值(tolerance),以决定接受某种颜色的宽松程度。
为了实现这个目标,将使用策略设计模式(Strategy Design Pattern)。这是一种面向对象的设计模式,非常适合将算法封装到类中。通过这种方式,可以轻松替换某个算法,或将多个算法串联在一起以构建更复杂的过程。此外,这种模式通过隐藏尽可能多的复杂性,为算法提供了一个直观的编程接口,从而简化了算法的部署和使用。
实现方案
#include <opencv2/opencv.hpp>
#include <iostream>class ColorDetector
{
public:void setTargetColor(int r, int g, int b); // 设置目标颜色void setColorDistanceThreshold(int distance); // 设置颜色距离阈值cv::Mat process(const cv::Mat &image); // 处理图像并返回结果private:bool matchColor(const cv::Vec3b& pixel) const; // 比较颜色private:cv::Vec3b targetColor; // 目标颜色int tolerance{30}; // 容差,默认值
};void ColorDetector::setTargetColor(int r, int g, int b)
{targetColor = cv::Vec3b(b, g, r); // 注意OpenCV中的颜色顺序是BGR
}void setColorDistanceThreshold(int distance) {if (distance < 0) distance = 0;tolerance= distance;}bool ColorDetector::matchColor(const cv::Vec3b& pixel) const
{return std::abs(pixel[0] - targetColor[0]) <= tolerance &&std::abs(pixel[1] - targetColor[1]) <= tolerance &&std::abs(pixel[2] - targetColor[2]) <= tolerance;
}cv::Mat ColorDetector::process(const cv::Mat &image)
{cv::Mat result(image.size(), CV_8UC1); // 输出的二值图像for (int i = 0; i < image.rows; ++i) {for (int j = 0; j < image.cols; ++j) {if (matchColor(image.at<cv::Vec3b>(i, j))) {result.at<uchar>(i, j) = 255; // 白色表示匹配} else {result.at<uchar>(i, j) = 0; // 黑色表示不匹配}}}return result;
}
#define IMAGE_1 "1.jpeg"
#define IMAGE_LOGO "logo.jpg"
#define IMAGE_LOGO_2 "logo_2.jpeg"int main()
{// 1. 创建图像处理器对象ColorDetector cdetect;// 2. 读取输入图像cv::Mat image = cv::imread(IMAGE_1);if (image.empty()) return 0;// 3. 设置输入参数cdetect.setTargetColor(230, 190, 130); // 这里假设要检测蓝色天空// 4. 处理图像并显示结果cv::namedWindow("result");cv::Mat result = cdetect.process(image);cv::imshow("result", result);cv::imshow("image", image);cv::waitKey();return 0;
}
OpenCV提供了一些内置函数可以完成类似的任务,例如提取具有特定颜色的连通区域。此外,策略设计模式的实现还可以通过函数对象进一步完善。最后,OpenCV定义了一个基类 cv::Algorithm,它实现了策略设计模式的核心概念。
计算两个颜色向量之间的距离
1. 简单方法:曼哈顿距离计算(Manhattan Distance)
计算方式是将每个颜色通道的绝对差值相加。
优点是实现简单且计算效率高,但在某些情况下可能无法准确反映颜色之间的感知差异。
return abs(color[0] - target[0]) +abs(color[1] - target[1]) +abs(color[2] - target[2]);
2.使用 OpenCV 的 cv::norm 函数
OpenCV 提供了一个名为 cv::norm 的函数,可以用来计算向量的欧几里得范数(Euclidean Norm)。我们可以使用它来计算颜色向量之间的距离:
return static_cast<int>(cv::norm(cv::Vec3i(color[0] - target[0],color[1] - target[1],color[2] - target[2])));
这里使用了 cv::Vec3i(一个包含三个整数值的向量),因为颜色通道相减的结果可能是负数,而 cv::Vec3b 是无符号类型,无法正确表示负数
通过这种方式,可以获得更精确的结果。需要注意的是,cv::norm 默认计算的是欧几里得距离。
3.使用 OpenCV 的 cv::absdiff 函数
错误示例
return static_cast<int>(cv::norm<uchar, 3>(color - target)); // 错误!
OpenCV 的算术运算符会调用 saturate_cast 函数来确保结果始终在输入类型的范围内(在这里是 uchar 类型)。如果目标值大于对应的颜色值,结果会被截断为 0,而不是预期的负值。这会导致距离计算不准确。
为了避免上述问题,可以使用 OpenCV 提供的 cv::absdiff 函数来计算两个向量之间的绝对差值:
// cv::absdiff 会逐元素地计算两个向量之间的绝对差值。
// cv::sum 函数会对结果向量的所有元素求和,返回一个标量值。
cv::Vec3b dist;
cv::absdiff(color, target, dist);
return cv::sum(dist)[0];
这种方法可以正确计算距离,但它需要两次函数调用(cv::absdiff 和 cv::sum),因此效率较低。
尽管 cv::absdiff 和 cv::sum 提供了一种正确且通用的解决方案,但对于大规模图像处理任务来说,效率可能是一个问题。在这种情况下,手动实现距离计算通常是更好的选择(使用方法1)
使用 OpenCV 函数实现颜色检测
上文展示了一种使用 OpenCV 函数实现颜色检测的方法。与手动编写循环相比,这种方法可以更快速地构建复杂的应用程序,并且通常具有更高的效率(得益于 OpenCV 的优化)。需要注意的是,当涉及多个中间步骤时,可能会消耗更多的内存。
实现方案
cv::Mat ColorDetector::process(const cv::Mat &image)
{cv::Mat output;// 1. 计算图像与目标颜色之间的绝对差值cv::absdiff(image, cv::Scalar(target), output);// 2. 将通道分离为三个独立的图像// 将 RGB 图像的三个通道分离出来,以便后续对每个通道单独处理std::vector<cv::Mat> images;cv::split(output, images);// 3. 将三个通道相加(可能发生饱和)// OpenCV 默认会对结果应用饱和操作(saturate_cast),因此如果某个像素的总和超过 255,它会被截断为 255。output = images[0] + images[1] + images[2];// 4. 应用阈值处理生成二值图像cv::threshold(output, // 输入/输出图像output, // 输出图像maxDist, // 阈值(必须小于 256)255, // 最大值cv::THRESH_BINARY_INV // 阈值模式);return output;
}
阈值处理:cv::threshold
cv::THRESH_BINARY_INV 模式:
- 像素值 ≤ 阈值时,设置为 255(白色)。
- 像素值 > 阈值时,设置为 0(黑色)。
其他有用的模式
- cv::THRESH_TOZERO:保留像素值大于阈值的部分,其余部分设为 0。
- cv::THRESH_TOZERO_INV:保留像素值小于或等于阈值的部分,其余部分设为 0。
使用 cv::floodFill 函数
上述案例中,使用了逐像素比较的方式来识别图像中颜色接近目标颜色的像素。
OpenCV 提供了一个功能类似的函数 cv::floodFill。cv::floodFill 的核心思想是基于连通区域进行颜色提取,而不仅仅是逐像素判断。
cv::floodFill 的工作原理
1.种子点(Seed Point)
- 需要指定一个起始像素位置(称为种子点),该点的颜色将作为参考颜色。
- 从种子点开始,算法会检查其邻居像素,并根据容差参数决定是否接受这些邻居像素。
2.连通性(Connectivity)
- 如果某个邻居像素被接受,则继续检查该像素的邻居,依此类推。
- 这样,算法可以提取出一个连通的颜色区域。
3.颜色容差(Tolerance Parameters)
- 容差参数决定了颜色相似度的标准。用户可以为高于和低于参考颜色的值分别设置不同的阈值。
- 在固定范围模式(cv::FLOODFILL_FIXED_RANGE)下,所有测试的像素都会与种子点的颜色进行比较。
- 在默认模式下,每个测试像素会与其邻居的颜色进行比较。
4.重新着色(Repainting)
- 被接受的像素会被重新着色为指定的颜色(通过第三个参数指定)。
实现方案
cv::Mat image = cv::imread("image.jpg");// 使用 floodFill 提取天空区域
cv::floodFill(image, // 输入/输出图像cv::Point(100, 50), // 种子点位置cv::Scalar(255, 255, 255), // 重新着色的颜色(白色)(cv::Rect*)0, // 返回的边界矩形(可选) 如果需要获取被填充区域的边界,可以传递一个非空指针cv::Scalar(35, 35, 35), // 颜色下限容差cv::Scalar(35, 35, 35), // 颜色上限容差cv::FLOODFILL_FIXED_RANGE); // 固定范围模式// cv::FLOODFILL_FIXED_RANGE:表示所有测试的像素都与种子点的颜色进行比较。// 默认模式(未指定 cv::FLOODFILL_FIXED_RANGE):表示每个测试像素与邻居的颜色进行比较
#include <opencv2/opencv.hpp>
#include <iostream>#define IMAGE_1 "1.jpeg"
#define IMAGE_LOGO "logo.jpg"
#define IMAGE_LOGO_2 "logo_2.jpeg"int main()
{cv::Mat image = cv::imread(IMAGE_1);if (image.empty()) return 0;cv::floodFill(image, // 输入/输出图像cv::Point(100, 50), // 种子点位置cv::Scalar(255, 255, 255), // 重新着色的颜色(白色)(cv::Rect*)0, // 返回的边界矩形(可选) cv::Scalar(35, 35, 35), // 颜色下限容差cv::Scalar(35, 35, 35), // 颜色上限容差cv::FLOODFILL_FIXED_RANGE); // 固定范围模式cv::imshow("image", image);cv::waitKey();return 0;
}
算法会从种子点开始,提取出一个连通的蓝色区域,并将其重新着色为白色。即使图像中其他地方存在颜色相似的像素,只要它们没有区域连通,就不会被识别。
应用场景
- 连通区域提取: 提取图像中特定颜色的连通区域(如天空、水面等)。
- 对象分割:将图像中的某个对象(如前景或背景)分割出来。
- 图像修复:填充图像中的小区域或孔洞。
- 交互式图像编辑:用户可以通过点击图像中的某个点来选择并操作特定区域。
使用函数对象
在 C++ 中,通过重载 operator() 运算符,可以创建一个类,使其实例像函数一样被调用。这种技术被称为函数对象(Functor) 。
优点是它们结合了函数的简洁性和类的状态管理能力,因此非常适合用于需要保存状态的算法。
实现方案
class ColorDetector
{
public:// 构造函数ColorDetector(uchar blue, uchar green, uchar red, int maxDist = 100): maxDist(maxDist) {setTargetColor(blue, green, red);}// 设置目标颜色void setTargetColor(uchar blue, uchar green, uchar red) {targetColor = cv::Vec3b(blue, green, red);}// 获取最大距离int getMaxDist() const {return maxDist;}// 设置最大距离void setMaxDist(int dist) {maxDist = dist;}// 重载 operator(),实现函数对象行为cv::Mat operator()(const cv::Mat &image) {cv::Mat output;// 计算绝对差值cv::absdiff(image, cv::Scalar(targetColor), output);// 分离通道并相加std::vector<cv::Mat> channels;cv::split(output, channels);output = channels[0] + channels[1] + channels[2];// 应用阈值处理cv::threshold(output, output, maxDist, 255, cv::THRESH_BINARY_INV);return output;}private:cv::Vec3b targetColor; // 目标颜色int maxDist; // 最大距离(容差)
};
// 创建 ColorDetector 实例,并初始化目标颜色和容差
ColorDetector colordetector(230, 190, 130, 100);// 使用函数对象检测颜色
cv::Mat image = cv::imread("image.jpg");
cv::Mat result = colordetector(image); // 像调用函数一样调用对象
适用场景
- 需要保存状态的算法。
- 动态配置参数的场景。
- 与 STL 算法结合使用的场景。
OpenCV 的算法基类:cv::Algorithm
cv::Algorithm 的核心功能
1. 动态创建算法实例
所有继承自 cv::Algorithm 的算法都可以通过静态方法动态创建。
确保算法在创建时总是处于有效状态(即未指定的参数会被赋予默认值)。
例如,对于 cv::ORB(一种用于检测兴趣点的算法),可以通过以下方式创建实例:
cv::Ptr<cv::ORB> ptrORB = cv::ORB::create(); // 默认状态
2. 读取和写入算法状态
cv::Algorithm 提供了通用的 read 和 write 方法,用于加载或存储算法的状态。
这使得算法可以轻松地保存到文件中,并在需要时重新加载。
cv::FileStorage fs("orb_state.yml", cv::FileStorage::WRITE);
ptrORB->write(fs); // 将算法状态写入文件
fs.release();cv::Ptr<cv::ORB> newORB = cv::ORB::create();
cv::FileStorage fs2("orb_state.yml", cv::FileStorage::READ);
newORB->read(fs2.root()); // 从文件中读取算法状态
fs2.release();
3.专用方法
每个算法都有其特定的功能方法。例如,cv::ORB 提供了 detect 和 compute 方法,用于检测特征点并计算描述符。
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
ptrORB->detectAndCompute(image, cv::Mat(), keypoints, descriptors);
这些方法是算法的核心计算单元,用户可以直接调用它们来执行特定任务。
4.参数设置与获取
算法通常包含多个内部参数,用户可以通过专用的 setter 和 getter 方法来调整这些参数。
ptrORB->setMaxFeatures(500); // 设置最大特征点数量
int maxFeatures = ptrORB->getMaxFeatures(); // 获取当前设置
cv::Algorithm 的多态性
如果将算法实例声明为 cv::Ptrcv::Algorithm 类型,则无法直接调用其专用方法(如 detect 或 compute)。这是因为 cv::Algorithm 是一个通用基类,它并不知道子类的具体方法。
cv::Ptr<cv::Algorithm> algo = cv::ORB::create();
// algo->detect(...) // 错误!detect 方法不可用
使用具体的子类类型(如 cv::Ptrcv::ORB)来声明指针,以便访问其专用方法。
cv::Ptr<cv::ORB> ptrORB = cv::ORB::create();
ptrORB->detectAndCompute(image, cv::Mat(), keypoints, descriptors); // 正确!
优点
- 统一的接口,便于管理和使用。
- 动态创建机制确保算法始终处于有效状态。
- 支持灵活的参数调整和状态管理。
- 易于扩展,适合开发新的算法。
适用场景
- 需要动态加载或保存算法状态的场景。
- 需要灵活调整算法参数的任务。
- 开发新的算法并希望与 OpenCV 现有框架无缝集成。
使用GrabCut算法分割图像
GrabCut 是一种流行的图像分割算法,特别适用于从静态图像中提取前景对象(例如,从一张图片中剪切并粘贴一个对象到另一张图片)。尽管它是一种复杂且计算成本较高的算法,但它通常能产生非常精确的结果。
图像分割实现步骤
1.定义输入图像和矩形区域
定义一个矩形区域来包含前景对象。所有在这个矩形之外的像素将被标记为背景。
// 定义包围矩形
cv::Rect rectangle(5, 70, 260, 120);
2.初始化 GrabCut 所需的数据结构
创建几个矩阵来存储分割结果和算法内部使用的模型。
cv::Mat result; // 分割结果(4种可能值) 每个像素可以有四种状态之一
cv::Mat bgModel, fgModel; // 算法内部使用的模型 用于存储算法生成的背景和前景模型
3.调用 cv::grabCut 函数
// GrabCut 分割
cv::grabCut(image, // 输入图像result, // 分割结果rectangle, // 包含前景对象的矩形bgModel, // 背景模型fgModel, // 前景模型5, // 迭代次数cv::GC_INIT_WITH_RECT); // 使用矩形模式 使用定义的矩形来指定前景区域
4.解释分割结果
分割结果图像中的每个像素可以有以下四种状态之一:
- cv::GC_BGD:背景像素(例如,在我们的例子中,矩形外的所有像素)。
- cv::GC_FGD:前景像素(在我们的例子中没有)。
- cv::GC_PR_BGD:可能是背景的像素。
- cv::GC_PR_FGD:可能是前景的像素(即,初始状态下矩形内的所有像素)。
为了生成二值分割图像,我们需要提取那些具有 cv::GC_PR_FGD 值的像素:
// 获取可能属于前景的像素
cv::compare(result, cv::GC_PR_FGD, result, cv::CMP_EQ);// 生成输出图像
cv::Mat foreground(image.size(), CV_8UC3, cv::Scalar(255, 255, 255));
image.copyTo(foreground, // 只复制前景像素result);
5.提取所有前景像素
如果要提取所有前景像素(包括 cv::GC_PR_FGD 和 cv::GC_FGD),可以通过检查第一个比特位来实现:
// 检查第一个比特位
result = result & 1; // 如果是前景,则值为1
因为 cv::GC_FGD 和 cv::GC_PR_FGD 的值分别为 1 和 3,而其他两个常量 cv::GC_BGD 和 cv::GC_PR_BGD 的值分别为 0 和 2。因此,通过与操作可以有效地提取前景像素。
实现方案
#include <opencv2/opencv.hpp>
#include <iostream>#define IMAGE_1 "1.jpeg"
#define IMAGE_LOGO "logo.jpg"
#define IMAGE_LOGO_2 "logo_2.jpeg"int main()
{cv::Mat image = cv::imread(IMAGE_1);if (image.empty()) {std::cerr << "Error: Could not load image!" << std::endl;return -1;}// 定义包含前景对象的矩形区域cv::Rect rectangle(50, 50, 300, 200); // 根据实际情况调整矩形位置和大小// 创建用于存储分割结果和模型的矩阵cv::Mat result; // 分割结果(4种可能值)cv::Mat bgModel, fgModel; // 背景和前景模型// 调用 GrabCut 算法cv::grabCut(image, // 输入图像result, // 分割结果rectangle, // 包含前景的矩形bgModel, // 背景模型fgModel, // 前景模型5, // 迭代次数cv::GC_INIT_WITH_RECT); // 使用矩形模式// 提取可能属于前景的像素cv::compare(result, cv::GC_PR_FGD, result, cv::CMP_EQ);// 创建一个空白背景图像(白色背景)cv::Mat foreground(image.size(), CV_8UC3, cv::Scalar(255, 255, 255));// 将原图中的前景像素复制到空白背景图像中image.copyTo(foreground, result);// 显示结果cv::imshow("Original Image", image);cv::imshow("Foreground", foreground);// 保存结果cv::imwrite("foreground.jpg", foreground);// 等待用户按键退出cv::waitKey(0);return 0;
}
转换颜色
RGB 颜色空间
RGB 颜色空间基于红(Red)、绿(Green)、蓝(Blue)三种加色原理。
在数字图像中,RGB 是默认的颜色空间,因为彩色图像是通过红、绿、蓝滤镜捕捉的。
RGB 的每个通道(红、绿、蓝)都经过归一化处理:当三个通道值相等时,会生成灰度颜色,从黑色(0,0,0)到白色(255,255,255)。然而,它并不适合用来直接衡量两种颜色之间的相似性。
RGB 的局限性
RGB 不是一个感知均匀的颜色空间。
在 RGB 空间中,两个颜色之间的距离可能无法准确反映它们在人眼中的视觉差异。例如:
- 两个颜色之间的距离相同,但它们看起来可能非常相似。
- 另外两个颜色之间的距离相同,但它们看起来却可能截然不同。
这种不一致性使得 RGB 颜色空间不适合用于需要精确比较颜色相似性的任务。
CIE Lab* 颜色空间
1.感知均匀性
- 在 CIE Lab* 中,两个颜色之间的欧几里得距离可以很好地反映它们在人眼中的视觉相似性。
- 距离越小,颜色看起来越相似;距离越大,颜色看起来差异越明显。
2.分量解释
- L* 表示亮度(Lightness),从黑到白的变化。
- a* 表示从绿色到红色的变化。
- b* 表示从蓝色到黄色的变化。
3.适用性
- 将图像从 RGB 转换到 CIE Lab* 后,可以直接用欧几里得距离来衡量颜色的相似性,而无需担心 RGB 空间的非均匀性问题。
颜色转换的步骤
1. 将图像从 BGR 转换到 CIE Lab 颜色空间
// 输入图像(假设为BGR格式)
cv::Mat inputImage = cv::imread("input.jpg");// 创建一个矩阵存储转换后的图像
cv::Mat labImage;// 使用 cv::cvtColor 进行颜色空间转换
cv::cvtColor(inputImage, labImage, cv::COLOR_BGR2Lab);
cv::COLOR_BGR2Lab 是 BGR 到 CIE Lab* 的转换代码。
转换后,labImage 包含了 Lab* 表示的图像数据。
2. 将目标颜色从 RGB 转换到 CIE Lab
void setTargetColor(unsigned char red, unsigned char green, unsigned char blue)
{// 创建一个单像素的临时图像cv::Mat tmp(1, 1, CV_8UC3);tmp.at<cv::Vec3b>(0, 0) = cv::Vec3b(blue, green, red); // 注意顺序:BGR// 转换到 CIE L*a*b*cv::cvtColor(tmp, tmp, cv::COLOR_BGR2Lab);// 提取转换后的颜色值cv::Vec3b labColor = tmp.at<cv::Vec3b>(0, 0);
}
tmp 是一个单像素图像,用于存储目标颜色。
转换后,labColor 包含了目标颜色的 CIE Lab* 值。
3. 在 CIE Lab 空间中比较颜色
在 CIE Lab* 空间中,颜色的距离可以直接用欧几里得距离计算:
// 计算两个颜色之间的欧几里得距离
double colorDistance(const cv::Vec3b &color1, const cv::Vec3b &color2)
{double deltaL = color1[0] - color2[0];double deltaA = color1[1] - color2[1];double deltaB = color1[2] - color2[2];return std::sqrt(deltaL * deltaL + deltaA * deltaA + deltaB * deltaB);
}
color1 和 color2 是两个 CIE Lab* 颜色。
返回值是两个颜色之间的感知距离。
其他常用颜色空间转换
转换类型 | 代码 | 描述 |
---|---|---|
BGR → 灰度 | cv::COLOR_BGR2GRAY | 将彩色图像转换为灰度图像 |
BGR → HSV | cv::COLOR_BGR2HSV | 转换到 HSV 颜色空间 |
BGR → YCrCb | cv::COLOR_BGR2YCrCb | 转换到 JPEG 压缩使用的 YCrCb 空间 |
BGR → CIE XYZ | cv::COLOR_BGR2XYZ | 转换到设备无关的 CIE XYZ 空间 |
BGR → CIE Luv* | cv::COLOR_BGR2Luv | 转换到另一种感知均匀颜色空间 |
// 将彩色图像转换为灰度图像
cv::Mat grayImage;
cv::cvtColor(colorImage, grayImage, cv::COLOR_BGR2GRAY);
工作原理
线性与非线性变换:
- RGB ↔ XYZ 是线性变换。
- RGB ↔ CIE Lab* 或 CIE Luv* 是非线性变换(为了实现感知均匀性)。
通道范围:
- CIE Lab*:
1. L 通道:亮度,范围 [0, 100],在 8 位图像中映射为 [0, 255]。
2. a 和 b 通道:色度,范围 [-127, 127],在 8 位图像中偏移为 [0, 255]。 - 灰度图像:单通道,范围 [0, 255]。
精度损失:
8 位图像在转换时会引入舍入误差,因此某些转换不可完全逆向。
用色调、饱和度和亮度表示颜色
最初考虑的是 RGB 颜色空间,尽管它在电子成像系统中是一种有效的颜色捕获和显示方式,但并不直观。
人们通常用色调、亮度或色彩浓度(即颜色是鲜艳还是柔和)来描述颜色。
因此,基于色调、饱和度和亮度的颜色空间被引入,以帮助用户通过更直观的属性来指定颜色。
颜色空间转换及可视化HSV各分量
HSV颜色空间简介
HSB 颜色空间通常用一个锥形来表示,其中内部的每个点对应一种特定的颜色。角度位置对应于颜色的色调,饱和度是与中心轴的距离,而亮度则由高度表示。锥形的顶点对应黑色,此时色调和饱和度是未定义的。
HSV(色调、饱和度、亮度)颜色空间是基于人类对颜色的自然感知而设计的。它将颜色分为三个直观属性:
色调 (Hue)
表示颜色的类型,例如红色、绿色、蓝色等。
在 HSV 中,色调用角度表示,范围为 [0, 360] 度。在 OpenCV 中,为了适应 8 位图像,范围被压缩到 [0, 180]。
饱和度 (Saturation)
表示颜色的纯度,即颜色中灰色的比例。
高饱和度对应鲜艳的颜色,低饱和度对应接近灰色的颜色。
范围为 [0, 1] 或 [0, 255](对于 8 位图像)。
亮度 (Value)
表示颜色的明暗程度。
在 OpenCV 中,亮度定义为 BGR 通道的最大值。
OpenCV 中的 HSV 实现
OpenCV 提供了两种主要的感知颜色空间:HSV 和 HLS。以下是它们的计算方式和实现细节:
- 亮度 (Value):
定义为 BGR 通道的最大值。 - 饱和度 (Saturation):
如果颜色为灰度(R=G=B),则饱和度为 0。
计算公式基于 BGR 的最大值和最小值:
S = (max(R, G, B) - min(R, G, B))/ max(R, G, B)
- 色调 (Hue):
基于 BGR 的最大值和最小值,通过三角函数计算角度。
红色对应 0 度,绿色对应 120 度,蓝色对应 240 度。
生成 HSV 色彩表
#include <opencv2/opencv.hpp>
#include <iostream>int main()
{// 创建一个 128x360 的三通道图像cv::Mat hs(128, 360, CV_8UC3);for (int h = 0; h < 360; h++) { // 遍历所有色调值for (int s = 0; s < 128; s++) { // 遍历所有饱和度值// 设置每个像素的 HSV 值hs.at<cv::Vec3b>(s, h)[0] = h / 2; // 色调 (0-180)hs.at<cv::Vec3b>(s, h)[1] = 255 - s * 2; // 饱和度 (从高到低)hs.at<cv::Vec3b>(s, h)[2] = 255; // 恒定亮度}}// 将 HSV 图像转换为 BGR 格式以显示cv::Mat hsvToBgr;cv::cvtColor(hs, hsvToBgr, cv::COLOR_HSV2BGR);// 显示结果cv::imshow("HSV Color Table", hsvToBgr);cv::waitKey(0);return 0;
}
修改图像亮度
#include <opencv2/opencv.hpp>
#include <iostream>#define IMAGE_1 "1.jpeg"
#define IMAGE_LOGO "logo.jpg"
#define IMAGE_LOGO_2 "logo_2.jpeg"int main()
{// 读取输入图像cv::Mat image = cv::imread(IMAGE_1);if (image.empty()) {std::cerr << "无法加载图像,请检查路径!" << std::endl;return -1;}// 转换为 HSV 颜色空间cv::Mat hsvImage;cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);// 分离 HSV 通道std::vector<cv::Mat> channels;cv::split(hsvImage, channels);// 将亮度通道设置为 255channels[2] = 255;// 合并通道cv::merge(channels, hsvImage);// 转换回 BGR 颜色空间cv::Mat newImage;cv::cvtColor(hsvImage, newImage, cv::COLOR_HSV2BGR);// 显示结果cv::imshow("Original Image", image);cv::imshow("Modified Image", newImage);cv::waitKey(0);return 0;
}
将BGR图像转换为HSV颜色空间
要将一个BGR格式的图像转换到HSV(色调Hue、饱和度Saturation、亮度Value)颜色空间,可以使用如下方法:
// 假设'image'是你的输入BGR图像
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
这里的CV_BGR2HSV是转换代码,它将BGR图像转换为HSV图像。
hsv变量现在存储了转换后的图像数据。
从HSV转回BGR颜色空间
cv::Mat bgr;
cv::cvtColor(hsv, bgr, CV_HSV2BGR);
分离并可视化HSV各通道
为了更好地理解HSV颜色空间中的每个组成部分,我们可以将HSV图像的三个通道拆分成独立的图像:
// 创建一个容器来保存三个通道
std::vector<cv::Mat> channels;
// 拆分HSV图像的三个通道
cv::split(hsv, channels);// 'channels[0]' 是色调(Hue)
// 'channels[1]' 是饱和度(Saturation)
// 'channels[2]' 是亮度(Value)
由于我们处理的是8位图像,OpenCV将这些通道的值调整到了0到255的范围(除了色调外,它的范围是0到180)。这使得我们可以像显示灰度图像一样显示这些通道。
实现案例
#include <opencv2/opencv.hpp>
#include <iostream>#define IMAGE_1 "1.jpeg"
#define IMAGE_LOGO "logo.jpg"
#define IMAGE_LOGO_2 "logo_2.jpeg"int main()
{// 读取输入图像cv::Mat image = cv::imread(IMAGE_1);if (image.empty()) {std::cerr << "无法加载图像,请检查路径!" << std::endl;return -1;}// 转换为HSV颜色空间cv::Mat hsvImage;cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);// 分离HSV的三个通道std::vector<cv::Mat> channels;cv::split(hsvImage, channels);// 提取各通道cv::Mat hue = channels[0]; // 色调 (Hue)cv::Mat saturation = channels[1]; // 饱和度 (Saturation)cv::Mat value = channels[2]; // 亮度 (Value)// 显示原始图像cv::imshow("Original Image", image);// 显示色调通道 (Hue)cv::imshow("Hue Channel", hue);// 显示饱和度通道 (Saturation)cv::imshow("Saturation Channel", saturation);// 显示亮度通道 (Value)cv::imshow("Value Channel", value);// 等待用户按键退出cv::waitKey(0);return 0;
}
使用颜色进行肤色检测
颜色信息可以用于特定对象的初步检测。
驾驶员辅助系统中,可以通过标准路标的颜色快速识别潜在的路标候选区域。
肤色检测中,可以用来判断图像中是否存在人类,并且常用于手势识别中通过肤色检测来定位手的位置。
使用颜色检测对象,通常需要以下步骤:
收集样本数据:
- 收集大量包含目标对象的图像样本,这些样本应从不同的视角和光照条件下捕获。
- 这些样本将用于定义分类器的参数。
选择颜色表示方式:
- 对于肤色检测,研究表明不同种族的肤色在色调(Hue)和饱和度(Saturation)空间中具有良好的聚类特性。因此,我们将使用色调和饱和度值来识别肤色
案例实现
#include <opencv2/opencv.hpp>
#include <iostream>#define IMG_WOMAN "princess.jpeg"
#define IMG_OLD_MAN "old_man.jpeg"// 定义肤色检测函数
void detectHScolor(const cv::Mat& image, // 输入图像double minHue, double maxHue, // 色调区间double minSat, double maxSat, // 饱和度区间cv::Mat& mask) // 输出掩码
{// 将图像转换为 HSV 空间cv::Mat hsv;cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);// 分离 HSV 通道std::vector<cv::Mat> channels;cv::split(hsv, channels);// channels[0] 是色调 (Hue)// channels[1] 是饱和度 (Saturation)// channels[2] 是亮度 (Value)// 色调掩码cv::Mat mask1; // 色调小于 maxHue 的部分cv::threshold(channels[0], mask1, maxHue, 255, cv::THRESH_BINARY_INV);cv::Mat mask2; // 色调大于 minHue 的部分cv::threshold(channels[0], mask2, minHue, 255, cv::THRESH_BINARY);cv::Mat hueMask; // 色调掩码if (minHue < maxHue)hueMask = mask1 & mask2; // 如果区间未跨越零度轴elsehueMask = mask1 | mask2; // 如果区间跨越零度轴// 饱和度掩码cv::Mat satMask; // 饱和度掩码cv::inRange(channels[1], minSat, maxSat, satMask);// 组合掩码mask = hueMask & satMask;
}int main()
{// 读取输入图像cv::Mat image = cv::imread(IMG_OLD_MAN);if (image.empty()) {std::cerr << "无法加载图像,请检查路径!" << std::endl;return -1;}// 定义肤色检测的色调和饱和度区间cv::Mat mask;detectHScolor(image, 160, 10, // 色调范围:320 度到 20 度(OpenCV 中缩放为 0-180)25, 166, // 饱和度范围:~0.1 到 ~0.65mask);// 显示检测结果cv::Mat detected(image.size(), CV_8UC3, cv::Scalar(0, 0, 0)); // 创建黑色背景image.copyTo(detected, mask); // 将检测到的区域复制到黑色背景上// 显示原始图像和检测结果cv::imshow("Original Image", image);cv::imshow("Detected Skin", detected);cv::waitKey(0);return 0;
}
相关文章:
opencv(C++)处理图像颜色
文章目录 介绍使用策略设计模式比较颜色实现方案计算两个颜色向量之间的距离1. 简单方法:曼哈顿距离计算(Manhattan Distance)2.使用 OpenCV 的 cv::norm 函数3.使用 OpenCV 的 cv::absdiff 函数错误示例 使用 OpenCV 函数实现颜色检测实现方…...
2025年焊接与热切割作业证考试真题分享
焊接与热切割作业属于特种作业操作证考试,理论知识点专业性强、安全规范要求高,如何高效备考成为关键!【100分题库】焊接与热切割作业理论备考题库紧扣最新考试大纲,帮你系统掌握考点,一次通过考试! 1、下…...
AI 代码生成工具如何突破 Java 单元测试效能天花板?
一、传统单元测试的四大痛点 时间黑洞:根据 JetBrains 调研,Java 开发者平均花费 35% 时间编写测试代码覆盖盲区:手工测试覆盖率普遍低于 60%(Jacoco 全球统计数据)维护困境:业务代码变更导致 38% 的测试用…...
【C++游戏引擎开发】第13篇:光照模型与Phong基础实现
一、Phong模型数学原理 1.1 光照叠加公式 L = k a I a + k d I d max ( 0 , n ⋅ l ) + k s I s max ( 0 , r ⋅ v ) α L = k_a I_a + k_d I_d \max(0, \mathbf{n} \cdot \mathbf{l}) + k_s I_s \max(0, \mathbf{r} \cdot \mathbf{v})^\alpha L=kaIa+kdIdmax(0…...
如何在Android系统上单编ko?
文章目录 一、先了解编译驱动需要什么?二、配置makefile1、在Android系统编译LOG上找到编译器信息(一般都会打印出来)2、基于源MK构造 可独立运行的makefile3)进入docker,在此makefile目录下敲make4)最后根…...
虚拟dom工作原理以及渲染过程
浏览器渲染引擎工作流程都差不多,大致分为5步,创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting 第一步,用HTML分析器,分析HTML元素,构建一颗DOM树(标记化和树构建)。 第二步,用…...
无人机视觉定位,常用相机,及相机提供的数据信息
常用相机类型 单目相机:仅使用一个摄像头进行图像采集,结构简单、成本低。它可以获取无人机前方或下方的二维图像信息,包括物体的形状、颜色、纹理等。双目相机:由两个摄像头组成,模拟人类双眼视觉原理,通…...
A2L文件解析
目录 1 摘要2 A2L文件介绍2.1 A2L文件作用2.2 A2L文件格式详解2.2.1 A2L文件基本结构2.2.2 关键元素与声明2.2.3 完整A2L文件示例 3 总结 1 摘要 A2L文件(也称为ASAP2文件)是ECU开发的核心接口文件,用于标定、测量和诊断的关键配置文件&…...
Ansible:role企业级实战
文章目录 实现 nginx 角色创建task文件创建handler文件准备模板文件创建变量文件在playbook中调用角色 实现 memcached 角色创建相关目录创建相关task任务准备模板文件查看目录结构在playbook中调用角色 实现多角色的选择 实现 nginx 角色 卸载httpd,创建相关目录 a…...
vue2使用vue-echarts
1.先安装echarts npm i echarts 2.安装vue-echarts 安装的时候注意下对应的版本 "echarts": "5.5.0", "vue-echarts": "6.7.3",这是我安装的版本 注意事项: 如果安装之后报错:"export watchEffect …...
多光谱相机:海洋管道漏油(溢油)监测
每年海上溢油和化工管道漏油造成的污染事故和经济损失频发,在生态方面,漏油会带来导致水质恶化、生态系统破坏、食物链受损。在经济方面,会造成渔业损失、旅游业损失、航运业损失。在健康方面,会造成食品安全问题,直接…...
Kaggle-Digit Recognizer-(多分类+卷积神经网络CNN)
Digit Recognizer 题意: 给你每个图片的dataframe类型的数据,让你预测出每个图片可能是多少。 思考: 数据处理 1.首先把数据从dadaframe转换成numpy,数据类型改为float32,并且并且展开为1维的28281的形状…...
jQuery多库共存
在现代Web开发中,项目往往需要集成多种JavaScript库或框架来满足不同的功能需求。然而,当多个库同时使用时,可能会出现命名冲突、功能覆盖等问题。幸运的是,jQuery提供了一些机制来确保其可以与其他库和谐共存。本文将探讨如何实现…...
MCU的USB接口作为 USB CDC串口输出
前言: 如下内容是和Chatgpt的问答对话。询问了Chatgpt 关于 MCU微控制器内部的USB端口作为串口输出是怎么工作的,是否需要在上位机上安装串口驱动程序等,Chatgpt解答的很好。 正文: STM32 使用USB作为串行设备端口,需…...
VCode 的 .S 汇编文件里面的注释不显示绿色
1. 确认文件语言模式 打开 .S 文件后,查看 VS Code 右下角的状态栏,确认当前文件的识别模式(如 Assembly、Plain Text 等)。如果显示为 Plain Text 或其他非汇编模式: 点击状态栏中的语言模式(如 Plain Te…...
【数学建模】(智能优化算法)萤火虫算法(Firefly Algorithm)详解与实现
萤火虫算法(Firefly Algorithm)详解与实现 文章目录 萤火虫算法(Firefly Algorithm)详解与实现前言1. 算法原理2. 算法流程3. Python实现4. 算法特点4.1 优点4.2 缺点 5. 应用领域6. 算法变种7. 总结与展望参考文献 前言 大家好,今天给大家介绍一种有趣且高效的群体…...
链路追踪组件学习
目录 1. 为啥需要链路追踪2. 常见的链路追踪组件3. 使用过的链路追踪组件3.1. Spring Cloud Sleuth3.2. Zipkin3.3. Apache SkyWalking 4. 集成Spring Cloud Sleuth框架4.1. 流程步骤4.2 sleuth工作流程 5. 集成zipKin5.1 添加 Zipkin 相关依赖5.2 安装zipkin服务5.3 配置 Zipk…...
# 基于OpenCV与Dlib的人脸融合技术实现
从仿射变换到人脸融合:基于OpenCV和Dlib的图像处理实践 在图像处理领域,仿射变换和人脸融合是两个非常有趣且实用的技术。仿射变换可以用于图像的几何变换,而人脸融合则可以创造出令人惊叹的视觉效果。本文将通过两个具体的代码示例…...
多光谱相机:水环境监测(水体富营养化、黑臭水体、藻类水华)
随着全球水体污染问题日益严峻,水体富营养化、黑臭水体和藻类水华等生态危机对人类健康和水生系统构成重大威胁。传统监测手段(如人工采样、单点传感器)因效率低、覆盖不足、实时性差等局限,难以满足复杂水环境的动态监管需求。多…...
记录一次nginx访问前端首页,一直显示nginx首页问题(实际是nginx访问页面权限问题)
同一台服务器,nginx配置是server { listen 8081; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location /New_mh_other { alias /home/hqu/data/new_mh_other; try…...
windows下命名管道双端通信
实现功能 1、命名管道双端通信(异步) 2、客户端断线重连 PS:多线程版本 PipeWrapper.h #include <windows.h> #include <string> #include <vector> #include "Utils/ThreadObject.h" #include "Utils/T…...
Linux自行实现的一个Shell(15)
文章目录 前言一、头文件和全局变量头文件全局变量 二、辅助函数获取用户名获取主机名获取当前工作目录获取最后一级目录名生成命令行提示符打印命令行提示符 三、命令处理获取用户输入解析命令行执行外部命令 四、内建命令添加环境变量检查和执行内建命令 五、初始化初始化环境…...
powerDesign 逆向 mysql 生成 物理模型,并用VBS脚本整理comment
学习自:https://www.cnblogs.com/xmyjcs/p/8536233.html 文章目录 Reverse Engineer格式化模型执行 VBS 脚本 Reverse Engineer 下面 DBMS 可以通过 ODBC(Open Database Connectivity,开放数据库连接)连接, 需要自己先…...
跨境全域中台:前端独立站群+后端共享云仓的协同作战体系
在全球化浪潮与互联网技术飞速发展的当下,跨境电商已然成为国际贸易领域中最为活跃的力量。据相关数据显示,过去几年跨境电商的年增长率持续保持高位,越来越多的企业投身于这片充满机遇与挑战的蓝海市场。在竞争日益激烈的跨境电商赛道上&…...
国产芯片解析:乐得瑞LDR6500C 超小封装全能芯片,赋能智能设备未来
LDR6500C是乐得瑞科技针对USB-C标准中的Bridge设备而开发的双USB-C DRP接口PD通信芯片,具备切换Data Role功能,支持最高USB PD 100W 充电,并且针对各大品牌设备的 USB-C 兼容性进行了特别优化,非常适合于 USB Type-C 设备快充转接…...
代码随想录-06-二叉树-05.10 二叉树的最小深度
二叉树的最小深度 #模板题 题目描述 给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明叶子节点是指没有子节点的节点 具体思路(暴力) 层序遍历;找到cur.left null && cur.ri…...
系统与网络安全------网络通信原理(6)
资料整理于网络资料、书本资料、AI,仅供个人学习参考。 应用层解析 DNS Domain Name System,域名系统 用来完成域名与IP地址之间的映射,便于用户对网站的记忆和访问 端口号为TCP或UDP的53 DNS工作原理 FTP File Transfer Protocol 文件…...
【Vue #2】脚手架 指令
一、脚手架 脚手架:一个保证各项工作顺利开展的平台,方便我们 拿来就用,零配置 1. Vue 代码开发方式 相比直接 script 引入 vue 源码,有没有更好的方式编写vue代码呢? ① 传统开发模式: 基于html文件开发Vue&…...
UE5 后坐力枪口上抬和恢复
文章目录 计算后坐力并让视角上抬后坐力回落 计算后坐力并让视角上抬 在玩家蓝图里,声明一个方法OnShootOnce,在武器每次射击时调用 1检测新的后坐力是否超过了最大后坐力,并选择一个小的 2 如何将角色模型设置为相机的子物体 3 最后记录一下当前的…...
Mysql B+树高度如何计算?
MySQL 的 InnoDB 存储引擎使用 B+树 作为索引结构,其高度增加会直接影响查询性能(每次高度增加意味着多一次磁盘 I/O)。以下是 B+树高度增加的 关键场景 和 优化建议: 1. B+树高度增加的触发条件 (1) 数据量持续增长 根本原因:B+树的层级由数据量(记录数)和每个节点的容…...
UE5 使用贴花创建弹孔
文章目录 使用射线检测击中点在击中点处创建贴花 使用射线检测击中点 和untiy一样,发射一条射线,在命中点处创建弹孔 在武器里定义射击检测方法 以下是对上边使用的方法的展开 GetShootStartPosition:获取射击起点 computeShootEndPosition:…...
程序持续内存泄漏问题定位参考
0 概括 本文用于记录 x86-Linux 应用程序发生持续性内存泄漏问题时的定位方法。主要介绍valgrind工具的应用。 1 原理 对于内存泄漏问题的定位,一种朴素的想法就是对内存申请点进行监控。对于一个内存申请调用点(例如c/c中的malloc函数)&a…...
Lumion 与 Enscape 怎么选?附川翔云电脑适配指南
建筑可视化领域,Lumion 和 Enscape 是两款主流实时渲染器,核心差异体现在操作逻辑、渲染特性及适用场景。结合川翔云电脑平台的硬件支持,可进一步优化使用体验。 一、核心差异:效率、操作与场景适配 1. 操作门槛与实时性 Lumio…...
WebShell详解:原理、分类、攻击与防御
目录 一、WebShell的定义与核心概念 二、WebShell的分类 三、WebShell的攻击原理与常见手法 1. 攻击原理 2. 常见攻击路径 四、WebShell的危害 五、防御与检测策略 六、总结 一、WebShell的定义与核心概念 WebShell是一种以ASP、PHP、JSP等网页脚本形式存在的恶…...
Ubuntu 24.04 中文输入法安装
搜狗输入法,在Ubuntu 24.04上使用失败,安装教程如下 https://shurufa.sogou.com/linux/guide 出现问题的情况,是这个帖子里描述的: https://forum.ubuntu.org.cn/viewtopic.php?t493893 后面通过google拼音输入法解决了&#x…...
数码视讯TR100系列/TR100-G1/TR100-G4/数码视讯F7-国科GK6323V100C芯片-刷机固件包
数码视讯TR100系列/数码视讯TR100-G1/数码视讯TR100-G4/数码视讯F7-国科GK6323V100C芯片-刷机固件包 刷机教程: 里面共有两种方法,一是TTL线刷烧录方法;二是卡刷固件包; 下面以数码视讯TR100-…...
Cloudflare教程:免费优化CDN加速配置,提升网站访问速度 | 域名访问缓存压缩视频图片媒体文件优化配置
1、启用 Tiered Cache 缓存开关:通过选择缓存拓扑,可以控制源服务器与 Cloudflare 数据中心的连接方式,以确保缓存命中率更高、源服务器连接数更少,并且 Internet 延迟更短。 2、增加浏览器缓存时间TTL:在此期间&#…...
24体育NBA足球直播M24模板自适应板源码
源码名称:体育直播赛事扁平自适应M24直播模板源码 开发环境:帝国cms7.5 空间支持:phpmysql 带软件采集,可以挂着自动采集发布,无需人工操作! 演示地址:https://www.52muban.com/shop/184022.h…...
dify+wan2.1搭建文生视频生成工具流
本文介绍在dify中使用阿里开源的Wan2.1 1.3B模型搭建文生视频工作流的方法。 使用的工具如下: 1、dify(官方:https://docs.dify.ai/zh-hans) 2、comfyui 一、comfyui安装 为了简单起见,本文介绍使用autodl完成comfyui的部署。在autodl创建实例,使用的镜像如下图: 大…...
C# js 判断table中tr否存在相同的值
html 中如: 实现:table数据表格中,点击删除按钮时,验证相同子订单号条数是否大于1,大于允许删除。保证数据表格中只有唯一的一条子订单号数据。 <table style"width: 100%; background-color: #fff;" ce…...
HTML5+CSS3小实例:纯CSS绘制七巧板
实例:纯CSS绘制七巧板 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale…...
什么是虚拟线程?与普通线程的区别
引言:线程的演进与挑战 在传统的并发编程中,线程是一种非常重要的概念。我们使用线程来实现任务的并发执行,从而提高程序的执行效率。普通线程(如 Thread 类)是一种重量级的线程,每个线程都对应着操作系统…...
Docker MySQL的主从同步 数据备份 数据同步 配置文件
创建主库 docker run \--namemysql_1 \-e MYSQL_ROOT_PASSWORD123456 \-p 3306:3306 \-v mysql_main_data:/var/lib/mysql \--restart unless-stopped \-d \mysql:8.0进入容器内部 docker exec -it mysql_1 bash查找配置文件 find / -name my.cnf复制出主机 docker cp mysql…...
PowerBI中的DATEDIFF函数
一、语法 DATEDIFF(开始日期,结束日期,日期时间类型),返回两个日期之间的时间差 二、示例 开始日期结束日期度量值计算结果2025/4/1 15:33:202025/4/1 15:33:30计算 DATEDIFF([开始日期],[结束日期],SECOND)102025/4/1 15:332025/4/1 15:3…...
C/C++共有的类型转换与c++特有的四种强制类型转换
前言 C 语言和 C 共有的类型转换: 自动类型转换(隐式类型转换): 编译器在某些情况下会自动进行的类型转换。强制类型转换(显示类型转换): 使用 (type)expression 或 type(expression) 语法进行…...
体验OceanBase的 并行导入功能
在数据库的日常使用中,会经常遇到以下场景: 数据复制:将一个或多个表中的数据复制到目标表中,可能是复制全部数据,也可能仅复制部分数据。数据合并:将数据从一个表转移到另一个表,或者将多…...
CSS的字体
在 CSS 中,字体(font)是网页设计中的一个重要部分,它控制了文本的外观和排版效果。通过设置不同的字体属性,我们可以使网页上的文字更具吸引力和可读性。以下是与字体相关的 CSS 属性及其用法: 1️⃣ font…...
开源模型应用落地-LangChain与MCP协议-集成GPT-4o构建下一代AI智能体的全栈实践(三)
一、前言 在人工智能技术快速迭代的今天,大型语言模型(LLM)如何高效集成外部工具与多模态能力,成为开发者面临的核心挑战。Anthropic推出的模型上下文协议(MCP)通过标准化工具接口,为AI应用提供了“即插即用”的生态基础,而LangChain凭借其模块化设计,正成为连接LLM…...
Qt 5.14.2入门(一)写个Hello Qt!程序
目录 参考链接:一、新建项目二、直接运行三、修改代码增加窗口内容1、Qt 显示一个 QLabel 标签控件窗口2、添加按键 参考链接: Qt5教程(一):Hello World 程序 Qt 编程指南 一、新建项目 1、新建一个项目(…...
FPGA_DDR(二)
在下板的时候遇到问题 1:在写一包数据后再读,再写再读 这时候读无法读出 查看时axi_arready没有拉高 原因 : 由于读地址后没有拉高rready,导致数据没有读出卡死现象。 解决结果...