Извлечение черных объектов из цветного фона

это легко для человеческих глаз, чтобы сказать черный из других цветов. Но как насчет компьютеров?

я напечатал несколько цветных блоков на обычной бумаге формата А4. Поскольку существует три вида чернил для создания цветного изображения: голубой, пурпурный и желтый, я устанавливаю цвет каждого блока C=20%, C=30%, C=40%, C=50%, а остальные два цвета равны 0. Это первая колонка моего исходного изображения. Пока никаких черных (K CMYK) чернила должны печататься. После этого я устанавливаю цвет из каждой точки K=100% и остальные цвета равны 0 для печати черных точек.

source

вы можете почувствовать, что мой образ странный и ужасный. На самом деле изображение увеличено в 30 раз и как чернила чит наши глаза хорошо видно. Цветные полосы мешают мне распознать эти черные точки (точка печатается как один пиксель в 800 dpi). Без цветового фона я привык blur и canny edge detector извлечь краю. Однако при добавлении цветового фона, просто do grayscale и edge detector не может получить хорошие результаты из-за полос. Как мои глаза справятся с решением таких проблем?

Я решил проверить яркость исходного изображения. Я ссылался в этой статье и Формула:

яркость = sqrt (0.299 R * R + 0.587 G * G + 0.114 B * B )

brightness

яркость более близко к человеческому восприятию и она работает очень хорошо в желтом фон, потому что яркость желтого цвета является самым высоким по сравнению с голубым и пурпурным. Но как сделать голубые и пурпурные полосы максимально яркими? Ожидаемый результат заключается в том, что все полосы исчезают.


более сложные изображения:

C=40%, M=40%

c40m40

C=40%, Y=40%

c40y40

Y=40%, M=40%

y40m40

результат FFT C=40%, Y=40% яркость изображения

c40y40 freq

кто-нибудь может дать мне несколько советов, чтобы удалить цветные полосы?


@natan я пробовал метод FFT вы предложили мне, но мне не повезло получить пик на обеих осях x и y. Чтобы построить частоту, как вы, я изменил размер изображения на квадрат.

c40m40

c40m40 freq

5 ответов


после проверки изображений я решил, что надежный порог будет более простым, чем что-либо. Например, глядя на фотографию C=40%, M=40%, я сначала перевернул интенсивности, поэтому черный (сигнал) будет белым, просто используя

im=(abs(255-im));

мы можем проверить его RGB гистограммы с помощью этого :

hist(reshape(single(im),[],3),min(single(im(:))):max(single(im(:)))); 
colormap([1 0 0; 0 1 0; 0 0 1]);

enter image description here

Итак, мы видим, что существует большой вклад в некоторую среднюю интенсивность, тогда как "сигнал", который теперь белый, в основном разделен на более высокое значение. Затем я применил простые пороги следующим образом:

thr = @(d) (max([min(max(d,[],1))  min(max(d,[],2))])) ;
for n=1:size(im,3)
    imt(:,:,n)=im(:,:,n).*uint8(im(:,:,n)>1.1*thr(im(:,:,n)));
end

imt=rgb2gray(imt);

и избавился от объектов меньше, чем некоторые типичные размеры области

min_dot_area=20;
bw=bwareaopen(imt>0,min_dot_area);
imagesc(bw); 
colormap(flipud(bone));

вот результат вместе с исходным изображением: enter image description here

происхождение этого порога от код я писал, что предполагал разреженные сигналы в виде 2-D пиков или сгустков на шумном фоне. Под редкостью я подразумевал отсутствие нагромождения вершин. В том случай, при проецировании max (image) на ось x или y (by (max(im,[],1) или (max(im,[],1) вы получаете хорошую меру фона. Это потому, что вы берете минимальную интенсивность max(im) вектор.

если вы хотите посмотреть на это по-другому, вы можете посмотреть на гистограмму интенсивностей изображения. Предполагается, что фон является нормальным распределением какого-то вида вокруг некоторой интенсивности, сигнал должен быть выше этой интенсивности, но с гораздо меньшим количеством вхождений. От найти max(im) одной из осей (x или y) вы обнаружите, какой был максимальный уровень шума.

вы увидите, что порог выбирает ту точку на гистограмме, где все еще есть некоторый шум над ней, но весь сигнал тоже выше нее. вот почему я настроил его на 1.1*thr. Наконец, есть много более причудливых способов получить надежный порог, это быстрый и грязный способ, который, на мой взгляд, достаточно хорош...


Я бы преобразовал изображение в цветовое пространство HSV, а затем использовал канал значений. Это в основном разделяет информацию о цвете и яркости.

Это изображение 50% cyan

enter image description here

затем вы можете просто сделать простой порог, чтобы изолировать точки.

enter image description here

Я просто сделал это очень быстро и я уверен, что вы могли бы получить лучшие результаты. Возможно, найти Контуры на изображении, а затем удалить любые контуры с небольшой площадью, фильтровать любой оставшийся шум.


спасибо всем за публикацию его ответа! После некоторого поиска и попытки я также придумал адаптивный метод извлечения этих черных точек из цветного фона. Кажется, что учитывая только яркость не смогла разрешить проблему совершенно. Поэтому Натанметод, который вычисляет и анализирует гистограмму RGB, является более надежным. К сожалению, я все еще не могу получить надежный порог для извлечения черных точек в других цветовых образцах, потому что вещи становятся все более и более непредсказуемыми, когда мы добавляем более глубокий цвет (например, Cyan > 60) или смешиваем два цвета вместе (например, Cyan = 50, Magenta = 50).

однажды я google "извлечь цвет" и извлечение цвета TinEye и цвета вора вдохновляют меня. Оба они очень крутое приложение, и изображение, обработанное бывшим веб-сайтом, именно то, что я хочу. Поэтому я решаю реализовать подобный материал самостоятельно. Алгоритм я использовал здесь k-средних кластеризации. И некоторые другие связанные ключевые слова для поиска могут быть color palette, color quantation и getting dominant color.

я сначала применяю гауссовский фильтр для сглаживания изображения.

GaussianBlur(img, img, Size(5, 5), 0, 0);

OpenCV поддерживает kmeans функция и она экономит мне много времени на кодировании. Я изменяю код.

// Input data should be float32 
Mat samples(img.rows * img.cols, 3, CV_32F);
for (int i = 0; i < img.rows; i++) {
    for (int j = 0; j < img.cols; j++) {
        for (int z = 0; z < 3; z++) {
            samples.at<float>(i + j * img.rows, z) = img.at<Vec3b>(i, j)[z];
        }
    }
}

// Select the number of clusters
int clusterCount = 4;
Mat labels;
int attempts = 1;
Mat centers;
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10, 0.1), attempts, KMEANS_PP_CENTERS, centers);

// Draw clustered result
Mat cluster(img.size(), img.type());
for (int i = 0; i < img.rows; i++) {
     for(int j = 0; j < img.cols; j++) { 
        int cluster_idx = labels.at<int>(i + j * img.rows, 0);
        cluster.at<Vec3b>(i, j)[0] = centers.at<float>(cluster_idx, 0);
        cluster.at<Vec3b>(i, j)[1] = centers.at<float>(cluster_idx, 1);
        cluster.at<Vec3b>(i, j)[2] = centers.at<float>(cluster_idx, 2);
    }
}
imshow("clustered image", cluster); 
// Check centers' RGB value
cout << centers;

после кластеризации я преобразую результат в оттенки серого и нахожу самый темный цвет, который, скорее всего, будет цвет черных точек.

// Find the minimum value
cvtColor(cluster, cluster, CV_RGB2GRAY);
Mat dot = Mat::zeros(img.size(), CV_8UC1);
cluster.copyTo(dot);
int minVal = (int)dot.at<uchar>(dot.cols / 2, dot.rows / 2);
for (int i = 0; i < dot.rows; i += 3) {
    for (int j = 0; j < dot.cols; j += 3) {
        if ((int)dot.at<uchar>(i, j) < minVal) {
            minVal = (int)dot.at<uchar>(i, j);
        }
    }
}
inRange(dot, minVal - 5 , minVal + 5, dot);
imshow("dot", dot);

давайте проверим два изображения.

(clusterCount = 4)

enter image description here

enter image description here

enter image description here

(clusterCount = 5)

enter image description here

enter image description here

enter image description here

один недостаток кластеризации k-средних-один фиксированный clusterCount не может быть применен к каждому изображению. Также кластеризация не так быстра для больших изображений. Это вопрос меня это очень раздражает. Мой грязный метод для лучшей производительности в реальном времени (на iPhone) - обрезать 1/16 изображения и кластеризировать меньшую площадь. Затем сравните все пиксели исходного изображения с каждым центром кластера и выберите пиксель, ближайший к "черному" цвету. Я просто вычислить евклидово расстояние между двумя цветами RGB.


простой метод-просто порог всех пикселей. Вот эта идея, выраженная в псевдокоде.

for each pixel in image
    if brightness < THRESHOLD
        pixel = BLACK
    else
        pixel = WHITE

или если вы всегда имеете дело с голубым, пурпурным и желтым фоном, то, возможно, вы можете получить лучшие результаты с критериями

if pixel.r < THRESHOLD and pixel.g < THRESHOLD and pixel.b < THRESHOLD

этот метод даст хорошие результаты только для простых изображений, где ничего, кроме черных точек, слишком темно.

вы можете поэкспериментировать со значением порога, чтобы найти хорошее значение для вашего изображения.


Я предлагаю преобразовать в цветовое пространство на основе цветности, например LCH, и отрегулируйте одновременные пороги на легковесности и chroma. Вот маска результата для L

enter image description here

Похоже, вам нужны адаптивные пороги, так как разные значения лучше всего работают для разных областей изображения.

вы также можете использовать HSV или HSL в качестве цветового пространства, но они менее однородны, чем LCH, полученные из Лаборатория.