В ваших изображениях у вас есть три фракции: образец (желто-красный), пламя (сине-белый) и фон (темный), однако образец и пламя перекрываются в той области, где вы хотите обнаружить край. Было бы неплохо отделить пламя от образца, и очевидный подход, кажется, разделяет его как-то по цвету. Я немного поиграл, и вот что я придумал.
Во-первых, преобразуйте изображение RGB в HSV, где мы имеем в основном только один цветной канал (оттенок).
hsv = rgb2hsv(img);
оттенок является периодическим, но в вашем случае красно-желтый образец, к сожалению, близок к нулю и близок к 1.
Сдвиньте границу оттенка, чтобы облако значений пикселей в пространстве HSV не было разделено.
h = hsv(:, :, 1);
h = mod(h + 0.5,1); % shift periodically
s = hsv(:, :, 2);
v = hsv(:, :, 3);
Давайте визуализируем это.
plot3(h(:), s(:), v(:), '.');
xlabel('hue');
ylabel('saturation');
zlabel('value');
Пламя и образец, кажется, хорошо разделены по оттенку (и частично также по насыщенности - пламя менее насыщено, чем образец).
Найдите центры скопления для пламени, образца и фона с помощью простого определения порога. Фон - это все со значением <0,2, в то время как пламя и образец имеют значение> 0,2, а пламя имеет оттенок <0,3, оттенок образца> 0,3.
T1 = 0.3; % threshold on hue (>T1 is sample)
T2 = 0.2; % threshold on value (<T2 is background)
m = h > T1 & v > T2;
sample = [mean(h(m)), mean(s(m)), mean(v(m))];
m = h < T1 & v > T2;
flame = [mean(h(m)), mean(s(m)), mean(v(m))];
m = v < T2;
background = [mean(h(m)), mean(s(m)), mean(v(m))];
C = [sample; flame; background];
Давайте посмотрим на средние векторы для образца, пламени и фона в пространстве ВПГ.
C =
0.55004 0.63657 0.79573
0.23729 0.50927 0.50652
0.42501 0.50855 0.085589
Теперь самое интересное. Давайте выразим изображение по пикселям как линейную комбинацию этих трех элементов.
Y = [h(:), s(:), v(:)];
x = max(Y / C, 0);
x = reshape(x, size(img));
Было бы лучше, если бы мы могли обеспечить плавность образца, пламени и фона, а также неотрицательность, но я не смог бы добиться этого в течение разумного периода времени и оставил это в качестве упражнения.
Давайте визуализируем это.
figure;
labels = {'sample', 'flame', 'background'};
for i = 1 : 3
subplot(1,3,i);
imagesc(x(:, :, i));
axis image;
title(labels{i});
end
colormap(gray);
Образец перекрытия и пламя были разделены (но перед дальнейшей обработкой требуется некоторое сглаживание). Видны некоторые артефакты сжатия JPEG, что несколько снизит точность результатов. Найдем ребра разделенного образца.
sample = imgaussfilt(x(:, :, 1), 3);
e = bwareafilt(edge(sample), 1);
Здесь дополнительно был сохранен только самый длинный край. Можно также использовать область пламени в качестве индикатора переднего края.
Давайте визуализируем исходное изображение вместе с обнаруженным краем.
figure;
[x, y] = ind2sub(size(e), find(e));
imshow(img);
hold on;
plot(y, x, 'g.', 'MarkerSize', 2);
Что выглядит разумно.
Последний кадр видео работает одинаково хорошо. Я получил хороший результат без дальнейшей настройки параметров, хотя это может помочь.
Как итог:
Пламя и образец были разделены по цвету (оттенок), затем на отделенный образец было применено общее обнаружение края. Пламя можно использовать как указание на интересную область для переднего края. Есть некоторые внутренние параметры в разделении (пороговые значения), а также в обнаружении краев (сглаживание, пороговые значения), которые необходимо настроить.