Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

178 lines
5.2 KiB
C++

#include "ColorReader.h"
#include "GlobalParams.h"
#include <cmath>
#include <algorithm>
ColorReader::ColorReader() {
}
ColorReader::~ColorReader() {
}
std::vector<ColorResult> ColorReader::readColors(
const cv::Mat& image,
const std::vector<cv::Point2f>& centers,
bool smallLocatorOnLeft,
bool applyWB) {
std::vector<ColorResult> results;
if (image.empty() || centers.empty()) {
return results;
}
// 复制图像用于颜色读取(避免绘图干扰)
cv::Mat colorImage = image.clone();
// 白平衡处理(可选)
if (applyWB) {
applyWhiteBalance(colorImage);
}
// 确定采样半径
int radius = GlobalParams::DEFAULT_SAMPLE_RADIUS;
// 采样每个色块的颜色
for (size_t i = 0; i < centers.size(); ++i) {
ColorResult result;
result.id = static_cast<int>(i);
// 采样颜色
cv::Scalar bgr = sampleColor(colorImage, centers[i], radius);
// 转换颜色空间
convertColorSpaces(bgr, result.h, result.s, result.v,
result.l, result.a, result.b);
result.r = bgr[2];
result.g = bgr[1];
result.b_val = bgr[0];
results.push_back(result);
}
// 调整顺序(确保从靠近小定位块的色块开始)
results = adjustOrder(results, smallLocatorOnLeft);
return results;
}
void ColorReader::applyWhiteBalance(cv::Mat& image) {
if (image.empty()) {
return;
}
// 确保是BGR格式
cv::Mat bgrImage;
if (image.channels() == 4) {
cv::cvtColor(image, bgrImage, cv::COLOR_BGRA2BGR);
} else if (image.channels() == 1) {
cv::cvtColor(image, bgrImage, cv::COLOR_GRAY2BGR);
} else {
bgrImage = image;
}
// 计算各通道均值
cv::Scalar sumChannels = cv::sum(bgrImage);
int totalPixels = bgrImage.rows * bgrImage.cols;
if (totalPixels == 0) {
return;
}
double avgB = sumChannels[0] / totalPixels;
double avgG = sumChannels[1] / totalPixels;
double avgR = sumChannels[2] / totalPixels;
// 避免除零
if (avgB < 1) avgB = 1;
if (avgG < 1) avgG = 1;
if (avgR < 1) avgR = 1;
// 计算目标均值(三通道平均)
double targetAvg = (avgB + avgG + avgR) / 3.0;
if (targetAvg < 1) {
return;
}
// 各通道缩放校正
std::vector<cv::Mat> channels(3);
cv::split(bgrImage, channels);
channels[0] = channels[0] * (targetAvg / avgB);
channels[1] = channels[1] * (targetAvg / avgG);
channels[2] = channels[2] * (targetAvg / avgR);
cv::merge(channels, bgrImage);
bgrImage.convertTo(image, CV_8UC3);
}
cv::Scalar ColorReader::sampleColor(const cv::Mat& image, const cv::Point2f& center, int radius) {
int cx = static_cast<int>(center.x);
int cy = static_cast<int>(center.y);
// 确保采样区域在图像内
int x1 = std::max(0, cx - radius);
int y1 = std::max(0, cy - radius);
int x2 = std::min(image.cols - 1, cx + radius);
int y2 = std::min(image.rows - 1, cy + radius);
if (x2 <= x1 || y2 <= y1) {
return cv::Scalar(0, 0, 0);
}
// 创建采样掩码(圆形)
cv::Mat roi = image(cv::Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1));
cv::Mat mask = cv::Mat::zeros(roi.size(), CV_8U);
cv::Point maskCenter(cx - x1, cy - y1);
cv::circle(mask, maskCenter, radius, cv::Scalar(255), -1);
// 计算掩码区域的平均颜色
cv::Scalar avgColor = cv::mean(roi, mask);
return avgColor;
}
void ColorReader::convertColorSpaces(const cv::Scalar& bgr,
double& h, double& s, double& v,
double& l, double& a, double& b) {
// 创建1x1像素的图像进行颜色转换
cv::Mat bgrMat(1, 1, CV_8UC3, cv::Scalar(bgr[0], bgr[1], bgr[2]));
// 转换到HSV
cv::Mat hsvMat;
cv::cvtColor(bgrMat, hsvMat, cv::COLOR_BGR2HSV);
cv::Vec3b hsv = hsvMat.at<cv::Vec3b>(0, 0);
h = hsv[0] * 2.0; // OpenCV的H范围是0-180,转换为0-360
s = hsv[1];
v = hsv[2];
// 转换到LAB
cv::Mat labMat;
cv::cvtColor(bgrMat, labMat, cv::COLOR_BGR2Lab);
cv::Vec3b lab = labMat.at<cv::Vec3b>(0, 0);
l = lab[0];
a = static_cast<double>(lab[1]) - 128.0; // a范围从0-255转为-128到127
b = static_cast<double>(lab[2]) - 128.0; // b范围从0-255转为-128到127
}
std::vector<ColorResult> ColorReader::adjustOrder(
std::vector<ColorResult>& colors,
bool smallLocatorOnLeft) {
// 如果小定位块在左边,颜色顺序已经是正确的
// 如果小定位块在右边,需要反转顺序
if (!smallLocatorOnLeft && colors.size() > 1) {
std::reverse(colors.begin(), colors.end());
}
// 重新编号
for (size_t i = 0; i < colors.size(); ++i) {
colors[i].id = static_cast<int>(i);
}
return colors;
}