Как эффективно находить и удалять полосы изменения интенсивности изображения размером 1 пиксель?

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

Использование чего-то вроде типичного обнаружения края Собеля в этом случае не сработает, потому что поверх такой полосы он обнаружит 0. Я могу подумать о других модификациях ядра, которые могли бы работать, например

-1 -2 -1
 2  4  2
-1 -2 -1

но я предположил, что, вероятно, существует «правильный» математический способ выполнения такой операции.

В конце концов, я хочу сгладить эти линии, используя окружающие пиксели (выборочное размытие). Эти линии могут появляться в любой ориентации, поэтому, если бы я использовал вышеприведенное ядро, мне нужно было бы применить его в обоих направлениях и добавить его, чтобы получить интенсивность линии, аналогичную при применении ядра Собеля.


person David    schedule 12.02.2015    source источник
comment
Вы на самом деле не говорите, в чем проблема с полосами - только то, что они контрастны и Собел не работает. Вы хотите удалить их? Улучшить их? Отметить их? Как они выглядят? Они горизонтальные или вертикальные?   -  person Mark Setchell    schedule 13.02.2015
comment
Я не понимаю, как фильтр Собеля не может обнаружить полосу в 1 пиксель. Если бы у нас была эта конкретная полоса, которая имеет более высокий контраст, чем полоса выше или ниже (или слева и справа, если это вертикальная полоса), то вы должны найти сильные градиенты вдоль этой полосы. Можете ли вы показать нам пример или код, который вы написали, чтобы проиллюстрировать вашу точку зрения? Я с трудом представляю, в чем проблема.   -  person rayryeng    schedule 13.02.2015
comment
@rayryeng - Проблема с Собелем в этом случае заключается в том, что если вы примените к линии шириной ровно один пиксель по яркости, вы получите ребро непосредственно на линии, а ребро на два пикселя выше и два пикселей ниже. изображение [-1,-2,-1, 0,0,0,1,2,1]. Линия будет лежать прямо на 0, а верхняя и нижняя будут лежать прямо на не-линии (для целей этого примера они будут одинаковыми). фактический край будет обнаружен прямо над линией и прямо под ней. Кроме того, я хочу сохранить края шириной не в один пиксель.   -  person David    schedule 13.02.2015


Ответы (1)


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

Я разработал для этого алгоритм, и он работает на моем примере данных (поскольку вы не дали никаких данных). Он состоит из двух частей:

Идентификация строк

Я не мог придумать простой, но эффективный фильтр для обнаружения линий (которые связаны, поэтому, вероятно, нужно смотреть на корреляции). Поэтому я использовал простой фильтр обнаружения одного пикселя:

-1 -1 -1
-1  8 -1
-1 -1 -1

а затем некоторое подходящее пороговое значение.

Экстраполяция данных из-за пределов маски в маску

Очень элегантное решение (используя только свертки) состоит в том, чтобы взять данные за пределами маски и свернуть их с помощью гаусса, затем взять отрицательную маску и свернуть ее с тем же самым гауссианом, а затем разделить оба попиксельно. Результатом внутри маски является желаемое размытие.

Что это математически: взвешенное усреднение данных.

Вот мои фантомные данные:

введите здесь описание изображения

А это идентификация линий

введите здесь описание изображения

И окончательный результат показывает, что искажение было подавлено в десять раз:

введите здесь описание изображения

И, наконец, мой код (в Matlab):

%% create phantom data with lines (1pixel wide bands)
[x, y] = ndgrid(1:100, 1:100);
original = 3 * x - 2 * y + 100 * sin(x / 2) + 120 * cos(y / 3); % funny shapes
bw = original > mean(original(:)); % black and white
distortion = bwmorph(bw,'remove'); % some lines
data = original + max(original(:)) * distortion; % phantom

% show
figure();
subplot(1,3,1); imagesc(original); axis image; colormap(hot); title('original');
subplot(1,3,2); imagesc(distortion); axis image; title('distortion');
subplot(1,3,3); imagesc(data); axis image; title('image');

%% line detection
% filter by single pixel filter
pixel_filtered = filter2([-1,-1,-1;-1,8,-1;-1,-1,-1], data);

% create mask by simple thresholding
mask = pixel_filtered > 0.2 * max(pixel_filtered(:)); 

% show
figure();
subplot(1,2,1); imagesc(pixel_filtered); axis image; colormap(hot); title('filtered');
subplot(1,2,2); imagesc(mask); axis image; title('mask');

%% line removal and interpolation
% smoothing kernel: gaussian
smooth_kernel = fspecial('gaussian', [3, 3], 1);
smooth_kernel = smooth_kernel ./ sum(smooth_kernel(:)); % normalize to one

% smooth image outside mask and divide by smoothed negative mask 
smoothed = filter2(smooth_kernel, data .* ~mask) ./ filter2(smooth_kernel, ~mask);

% withing mask set data to smoothed
reconstruction = data .* ~mask + smoothed .* mask;

% show
figure();
subplot(1,3,1); imagesc(reconstruction); axis image; colormap(hot); title('reconstruction');
subplot(1,3,2); imagesc(original); axis image; title('original');
subplot(1,3,3); imagesc(reconstruction - original); axis image; title('difference');
person Trilarion    schedule 13.02.2015
comment
Выполняя базовый анализ на бумаге одного пикселя со значениями 0 или 1 с обоими нашими ядрами, они, по-видимому, имеют одинаковый контраст на горизонтальных и вертикальных линиях, но моя версия выглядит лучше на диагональных линиях. Тем не менее проголосовал и помечен как ответ, потому что он работает за один проход и является очень хорошо продуманным ответом. - person David; 13.02.2015
comment
@ Дэвид Спасибо. И вы можете свободно все комбинировать и, может быть, использовать только мою экстраполяцию. Не знаю, используете ли вы Matlab, Python или C++. - person Trilarion; 13.02.2015
comment
Python - это то, что я использовал. Я закодировал это и сейчас пробую оба наших на нашем реальном наборе данных. - person David; 13.02.2015
comment
Похоже, что оба ядра работают примерно одинаково в нашем реальном наборе данных. Спасибо еще раз за помощь! - person David; 13.02.2015