Сегментация изображения по цвету фона-OpenCV Android
Я пытаюсь сегментировать визитные карточки и разделить их по цвету фона, чтобы рассматривать их как разные области интереса.
например, карта такого рода:
должна быть разделена на два изображения, так как есть 2 цвета фона. Есть ли какие-либо предложения по решению этой проблемы? Я пробовал сделать контурный анализ, который оказался не слишком успешным.
другие примеры карт:
эта карта должна дайте 3 сегментации, так как есть три части, хотя это только 2 цвета (хотя 2 цвета будут в порядке).
вышеуказанная карта должна дать только одну сегментацию, поскольку это всего лишь один цвет фона.
Я пока не пытаюсь думать о градиентных фонах.
2 ответов
Это зависит от того, как выглядят другие карты, но если все изображения находятся в этом отличном качестве, это не должно быть слишком сложно.
в опубликованном примере вы можете просто собрать цвета пикселей границы (самый левый столбец, самый правый столбец, первая строка, последняя строка) и обработать то, что вы найдете, как возможные цвета фона. Возможно, проверьте, достаточно ли пикселей примерно одного цвета. Тебе нужно измерить расстояние. Одно простое решение-просто использовать евклидову расстояние в цветовом пространстве RGB.
более общим решением было бы найти кластеры в цветовых гистограммах всего изображения и обработать каждый цвет (опять же с допуском), который имеет более x% от общего количества пикселей в качестве цвета фона. Но то, что вы определяете как фон, зависит от того, чего вы хотите достичь и как выглядят ваши изображения.
Если вам нужны дополнительные предложения, вы можете опубликовать больше изображений и пометить, какие части изображений вы хотите обнаружить как цвет фона и какой парст нет.
-
Edit: ваши два новых изображения также показывают тот же шаблон. Цвета фона занимают большую часть изображения, нет шума и нет цветовых градиентов. Такой простой подход может выглядеть следующим образом:
вычислить гистограмму изображения: см. http://docs.opencv.org/modules/imgproc/doc/histograms.html#calchist и http://docs.opencv.org/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html
найдите наиболее заметные цвета на гистограмме. Если вы не хотите перебирать коврик самостоятельно, вы можете использовать minMaxLoc (http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#minmaxloc), как показано в документации calchist (см. выше), и если цвет занимает достаточно процент от количества пикселей сохранить это и установите соответствующий bin в гистограмме на ноль. Повторяйте до тех пор, пока ваш процент не будет достигнут. Затем вы сохраните список наиболее заметных цветов, ваших фоновых цветов.
порог изображения для каждого цвета фона у вас. Смотрите: http://docs.opencv.org/doc/tutorials/imgproc/threshold/threshold.html
в результате threadholded изображений найти соответствующий регионов цвет фона. Смотрите: http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html
Если у вас есть примеры, которые не работают с таким подходом, просто опубликовать их.
в качестве подхода для поиска фонов с цветовыми градиентами в них можно использовать canny. Следующий код (да, не android, я знаю, но результат должен быть таким же, если вы его портируете) отлично работает с тремя образцами, которые вы опубликовали до сих пор. Если у вас есть другие изображения, которые не работают с этим, пожалуйста, дайте мне знать.
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
Mat src;
Mat src_gray;
int canny_thresh = 100;
int max_canny_thresh = 255;
int size_per_mill = 120;
int max_size_per_mill = 1000;
RNG rng(12345);
bool cmp_contour_area_less(const vector<Point>& lhs, const vector<Point>& rhs)
{
return contourArea(lhs) < contourArea(rhs);
}
void Segment()
{
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Canny(src_gray, canny_output, canny_thresh, canny_thresh*2, 3);
// Draw rectangle around canny image to also get regions touching the edges.
rectangle(canny_output, Point(1, 1), Point(src.cols-2, src.rows-2), Scalar(255));
namedWindow("Canny", CV_WINDOW_AUTOSIZE);
imshow("Canny", canny_output);
// Find the contours.
findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
// Remove largest Contour, because it represents always the whole image.
sort(contours.begin(), contours.end(), cmp_contour_area_less);
contours.resize(contours.size()-1);
reverse(contours.begin(), contours.end());
// Maximum contour size.
int image_pixels(src.cols * src.rows);
cout << "image_pixels: " << image_pixels << "\n";
// Filter the contours, leaving just large enough ones.
vector<vector<Point> > background_contours;
for(size_t i(0); i < contours.size(); ++i)
{
double area(contourArea(contours[i]));
double min_size((size_per_mill / 1000.0) * image_pixels);
if (area >= min_size)
{
cout << "Background contour " << i << ") area: " << area << "\n";
background_contours.push_back(contours[i]);
}
}
// Draw large contours.
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
for(size_t i(0); i < background_contours.size(); ++i)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
drawContours(drawing, background_contours, i, color, 1, 8, hierarchy, 0, Point());
}
namedWindow("Contours", CV_WINDOW_AUTOSIZE);
imshow("Contours", drawing);
}
void size_callback(int, void*)
{
Segment();
}
void thresh_callback(int, void*)
{
Segment();
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
cout << "Please provide an image file.\n";
return -1;
}
src = imread(argv[1]);
cvtColor(src, src_gray, CV_BGR2GRAY);
blur(src_gray, src_gray, Size(3,3));
namedWindow("Source", CV_WINDOW_AUTOSIZE);
imshow("Source", src);
if (!src.data)
{
cout << "Unable to load " << argv[1] << ".\n";
return -2;
}
createTrackbar("Canny thresh:", "Source", &canny_thresh, max_canny_thresh, thresh_callback);
createTrackbar("Size thresh:", "Source", &size_per_mill, max_size_per_mill, thresh_callback);
Segment();
waitKey(0);
}