1.颜色空间-RGB
//颜色通道
Mat-rgb
0-r,1-g,2-b
Mat-bgr
0-b,1-g,2-r
//通道分离:
vector<Mat>channels_;
split(img, channels_);
生成随机颜色:
随机颜色范围:0.0~255.0
RNG rng;
b=rng.uniform(0.0, 255.0);
g=rng.uniform(0.0, 255.0);
r=rng.uniform(0.0, 255.0);
Scalar color_ = Scalar(b,g,r);
2.颜色空间-HSV
倒锥形模型:
HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间。这个模型就是按色彩、深浅、明暗来描述的。
色调H
用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;
色调图谱:
饱和度S
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
明度V
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
RGB和CMY颜色模型都是面向硬件的,而HSV(Hue Saturation Value)颜色模型是面向用户的。
HSV模型的三维表示从RGB立方体演化而来。设想从RGB沿立方体对角线的白色顶点向黑色顶点观察,就可以看到立方体的六边形外形。六边形边界表示色彩,水平轴表示纯度,明度沿垂直轴测量。
相互转换:
RGB->HSV:
HSV->RGB:
实现:
参考:色彩转换系列之RGB格式与HSV格式互转原理及实现
Mat RGB2HSV(Mat src) {
int row = src.rows;
int col = src.cols;
Mat dst(row, col, CV_32FC3);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
float b = src.at<Vec3b>(i, j)[0] / 255.0;
float g = src.at<Vec3b>(i, j)[1] / 255.0;
float r = src.at<Vec3b>(i, j)[2] / 255.0;
float minn = min(r, min(g, b));
float maxx = max(r, max(g, b));
dst.at<Vec3f>(i, j)[2] = maxx; //V
float delta = maxx - minn;
float h, s;
if (maxx != 0) {
s = delta / maxx;
}
else {
s = 0;
}
if (r == maxx) {
h = (g - b) / delta;
}
else if (g == maxx) {
h = 2 + (b - r) / delta;
}
else if (b==maxx) {
h = 4 + (r - g) / delta;
}
else{
h = 0;
}
h *= 60;
if (h < 0)
h += 360;
dst.at<Vec3f>(i, j)[0] = h;
dst.at<Vec3f>(i, j)[1] = s;
}
}
return dst;
}
Mat HSV2RGB(Mat src) {
int row = src.rows;
int col = src.cols;
Mat dst(row, col, CV_8UC3);
float r, g, b, h, s, v;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
h = src.at<Vec3f>(i, j)[0];
s = src.at<Vec3f>(i, j)[1];
v = src.at<Vec3f>(i, j)[2];
if (s == 0) {
r = g = b = v;
}
else {
h /= 60;
int offset = floor(h);
float f = h - offset;
float p = v * (1 - s);
float q = v * (1 - s * f);
float t = v * (1 - s * (1 - f));
switch (offset)
{
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
default:
break;
}
}
dst.at<Vec3b>(i, j)[0] = int(b * 255);
dst.at<Vec3b>(i, j)[1] = int(g * 255);
dst.at<Vec3b>(i, j)[2] = int(r * 255);
}
}
return dst;
}
3.图像中绘制填充背景色的矩形
void draw_(const cv::Mat&img)
{
cv::Mat dst = img.clone();
cv::Point p1 = cv::Point(50, 30);
cv::Point p2 = cv::Point(100, 70);
cv::Rect rect_0(p1, p2);
cv::Mat mask(dst.size(), dst.type(), cv::Scalar::all(0));
cv::Mat roi_0(rect_0.size(), dst.type(), cv::Scalar::all(0));
cv::rectangle(mask, rect_0, cv::Scalar(180, 238, 120), -1);
cv::addWeighted(dst(rect_0), 0.7, mask(rect_0), 0.3, 0, roi_0);
roi_0.copyTo(dst(rect_0));
cv::namedWindow("mask", cv::WINDOW_NORMAL);
cv::imshow("mask", dst);
cv::waitKey(0);
}
4.二值化:
输入应为灰度图
//大津阈值
threshold(gray, bw, -1, 255,THRESH_OTSU);//
//可调节阈值T
threshold(gray, bw, T, 255,THRESH_BINARY_INV);//
或者:
Mat bw0(img.size(), CV_8UC1, Scalar::all(0));
bw0.setTo(255, gray == 255);
对连通域进行分析
Mat label, stats, cens;
connectedComponentsWithStats(bw0, label, stats, cens, 8);
//统计面积
Mat bw1(gray.size(), CV_8UC1, Scalar(0));
int area_T = 0.;
vector<int>indexes;
for (int i = 0; i < stats.rows; ++i)
{
int* ptr = stats.ptr<int>(i);
if (ptr[4] >=10 && ptr[4] < 150)
{
indexes.push_back(i);
bw1.setTo(255, label == i);
//cout << i << " "<< stats.at<int>(i, CC_STAT_AREA)<< endl;
}
}
5.轮廓查找
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(bw1, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
Mat bw2(gray.size(), CV_8UC1, Scalar(0));
for (int i = 0; i < contours.size(); ++i)
{
RotatedRect rect = minAreaRect(contours[i]);
if (contourArea(contours[i]) / rect.size.area() > 0.8)
{
drawContours(bw2, contours, i, Scalar(255), -1);
}
cv::Point2f vertex[4];
rect.points(vertex);
for (int i = 0; i < 4; i++) {
cv::circle(mask, points[i], 4, cv::Scalar(0, 255, 0), -1);
cv::line(mask, vertex[i], vertex[(i + 1) % 4],cv::Scalar(100, 200, 211), 1);
}
//计算长度
int len=cv::arcLength(contours[i],1);
}
//轮廓按照面积排序
std::sort(contour.begin(),contour.end(),[](const std::vector<Point> &s1,
const std::vector<Point> &s2){
double a1=contourArea(s1);
double a2=contourArea(s2);
return a1>a2;
});
//轮廓按照大小排序
std::sort(contour.begin(),contour.end(),[](const std::vector<Point> &s1,
const std::vector<Point> &s2){return s1.size()>s2.size();});
//对点集按照坐标x排序
sort(points_.begin(), points_.end(), [=](cv::Point2f& a, cv::Point2f& b)
{return a.x < b.x; });
//对点集按照坐标y排序
sort(points_.begin(), points_.end(), [=](cv::Point2f& a, cv::Point2f& b)
{return a.y < b.y; });