[英]How to count pixels in color segment in OpenCV
我有一个OpenCV C ++应用程序。 我已经用pyrMeanShiftFiltering函数对图像进行了分割。 现在,我需要计算一个段中的像素和同一段中具有最频繁值的像素数,以便计算它们之间的比率。 我该怎么办?
Mat image, segmented;
image = imread("TsukubaL.jpg", 1 );
pyrMeanShiftFiltering(image, segmented, 16, 32);
分割后的图像为:
如果考虑单个段中的像素,则我对该段中的像素进行计数的部分是:
int cont=0;
Vec3b x = segmented.at<Vec3b>(160, 136);
for(int i = 160; i < segmented.rows; ++i) { //check right-down
for(int j = 136; j < segmented.cols; ++j) {
if(segmented.at<Vec3b>(i, j) == x)
cont++;
else
continue;
}
}
for(int i = 160; i > 0; --i) { //check right-up
for(int j = 136; j < segmented.cols; ++j) {
if(segmented.at<Vec3b>(i, j) == x)
cont++;
else
continue;
}
}
for(int i = 160; i < segmented.rows; ++i) { //check down-left
for(int j = 136; j > 0; --j) {
if(segmented.at<Vec3b>(i, j) == x)
cont++;
else
continue;
}
}
for(int i = 160; i > 0; --i) { //check up-left
for(int j = 136; j > 0; --j) {
if(segmented.at<Vec3b>(i, j) == x)
cont++;
else
continue;
}
}
cout<<"Pixel "<<x<<"cont = "<<cont<<endl;
在此示例中,我考虑位置(160,136)处的白色像素,并从该像素开始在四个方向上将相同像素计数到中心像素,输出为:
像素[206,222,240] cont = 127
这可能是一个好的方法吗?
首先,您需要定义一个具有与初始点相同颜色(此处称为种子 )的像素的蒙版。 您可以使用给定公差的inRange
。 假设种子在头上 ,您将得到类似以下内容的东西:
现在,您需要找到包含种子的已连接组件。 您可以通过多种方式执行此操作。 在这里,我修改了生成标记算法(可以在此处找到)。 您将获得包含种子的斑点的点列表。 然后可以使用以下几点制作蒙版:
现在您已经拥有了所有点,现在可以轻松地找到线段中的点数。 要查找最常见的颜色,可以使用该段中包含的BGR值制作直方图。 由于具有所有RGB值的直方图将具有256 * 256 * 256个bin,因此使用地图更为实用。 我修改了此处找到的代码,以使用给定的蒙版制作直方图。
现在,您只需要查找频率较高的颜色值即可。 对于此示例,我得到了:
# points in segment: 2860
Most frequent color: [209, 226, 244] #: 168
看一下代码:
#include <opencv2/opencv.hpp>
#include <vector>
#include <stack>
#include <map>
using namespace cv;
using namespace std;
vector<Point> connected_components(const Mat1b& img, Point seed)
{
Mat1b src = img > 0;
int label = 0;
int w = src.cols;
int h = src.rows;
int i;
cv::Point point;
// Start from seed
std::stack<int, std::vector<int>> stack2;
i = seed.x + seed.y*w;
stack2.push(i);
// Current component
std::vector<cv::Point> comp;
while (!stack2.empty())
{
i = stack2.top();
stack2.pop();
int x2 = i%w;
int y2 = i / w;
src(y2, x2) = 0;
point.x = x2;
point.y = y2;
comp.push_back(point);
// 4 connected
if (x2 > 0 && (src(y2, x2 - 1) != 0))
{
stack2.push(i - 1);
src(y2, x2 - 1) = 0;
}
if (y2 > 0 && (src(y2 - 1, x2) != 0))
{
stack2.push(i - w);
src(y2 - 1, x2) = 0;
}
if (y2 < h - 1 && (src(y2 + 1, x2) != 0))
{
stack2.push(i + w);
src(y2 + 1, x2) = 0;
}
if (x2 < w - 1 && (src(y2, x2 + 1) != 0))
{
stack2.push(i + 1);
src(y2, x2 + 1) = 0;
}
// 8 connected
if (x2 > 0 && y2 > 0 && (src(y2 - 1, x2 - 1) != 0))
{
stack2.push(i - w - 1);
src(y2 - 1, x2 - 1) = 0;
}
if (x2 > 0 && y2 < h - 1 && (src(y2 + 1, x2 - 1) != 0))
{
stack2.push(i + w - 1);
src(y2 + 1, x2 - 1) = 0;
}
if (x2 < w - 1 && y2>0 && (src(y2 - 1, x2 + 1) != 0))
{
stack2.push(i - w + 1);
src(y2 - 1, x2 + 1) = 0;
}
if (x2 < w - 1 && y2 < h - 1 && (src(y2 + 1, x2 + 1) != 0))
{
stack2.push(i + w + 1);
src(y2 + 1, x2 + 1) = 0;
}
}
return comp;
}
struct lessVec3b
{
bool operator()(const Vec3b& lhs, const Vec3b& rhs) {
return (lhs[0] != rhs[0]) ? (lhs[0] < rhs[0]) : ((lhs[1] != rhs[1]) ? (lhs[1] < rhs[1]) : (lhs[2] < rhs[2]));
}
};
map<Vec3b, int, lessVec3b> getPalette(const Mat3b& src, const Mat1b& mask)
{
map<Vec3b, int, lessVec3b> palette;
for (int r = 0; r < src.rows; ++r)
{
for (int c = 0; c < src.cols; ++c)
{
if (mask(r, c))
{
Vec3b color = src(r, c);
if (palette.count(color) == 0)
{
palette[color] = 1;
}
else
{
palette[color] = palette[color] + 1;
}
}
}
}
return palette;
}
int main()
{
// Read the image
Mat3b image = imread("tsukuba.jpg");
// Segment
Mat3b segmented;
pyrMeanShiftFiltering(image, segmented, 16, 32);
// Seed
Point seed(140, 160);
// Define a tolerance
Vec3b tol(10,10,10);
// Extract mask of pixels with same value as seed
Mat1b mask;
inRange(segmented, segmented(seed) - tol, segmented(seed) + tol, mask);
// Find the connected component containing the seed
vector<Point> pts = connected_components(mask, seed);
// Number of pixels in the segment
int n_of_pixels_in_segment = pts.size();
Mat1b mask_segment(image.rows, image.cols, uchar(0));
for (const auto& pt : pts)
{
mask_segment(pt) = uchar(255);
}
// Get palette
map<Vec3b, int, lessVec3b> palette = getPalette(segmented, mask_segment);
// Get most frequent color
Vec3b most_frequent_color;
int freq = 0;
for (const auto& pal : palette)
{
if (pal.second > freq)
{
most_frequent_color = pal.first;
freq = pal.second;
}
}
cout << "# points in segment: " << n_of_pixels_in_segment << endl;
cout << "Most frequent color: " << most_frequent_color << " \t#: " << freq << endl;
return 0;
}
在按照上一个答案所示或通过任何其他方式创建所需的蒙版之后,可以在蒙版图像周围创建轮廓。 这将使您可以使用轮廓轮廓函数直接计算段内的像素数。
您可以将所选区域划分为一个新的子垫,并在该子垫上计算直方图以获得最频繁的值。 如果仅关注颜色值而不关注亮度值,则还应根据要求将图像转换为HSV,LAB或YCbCr颜色空间。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.