# Image thresholding while retaining “underneath” contents

I have sample images, and I need to extract certain features that depend on color. Images are more like line art, contours, and consist of various line forms, and several colors.

I can extract specific features through thresholding, but if lines intersect I always get discontinuities (holes) that are not acceptable. As example I prepared this:

• I have image with two "lines":

• I'll threshold to remove black, but underneath line (which of course doesn't exist as information in bitmap representation) gets discontinued:

• while I want it like this:

which I'll later equalize, and get wanted context.

I used image processing program, and would appreciate some tip how to do this in Gimp or Photoshop, but any answer is welcomed like Python/ndimage, Matlab/Octave...

-
You want to separate two overlapping transparent images, is that right? A complete example image is welcome. – Emre Feb 20 '13 at 0:57
In your example you see two bars, but there is really three. Your brain uses some higher level processes, joining up the edges to give that perception. Any method would also have to do this an be quite complicated. – geometrikal Feb 20 '13 at 3:21
@Emre: No, those a scans of various topological maps. – zetah Feb 20 '13 at 10:20
@geometrikal: I'm aware of that, and already noted that such information does not exist. I thought that there is known algorithm that can consider bitmap image with "lines" as content. I'll try skeletonizing and some thinning methods later, to see if I can retain color in the process, and afterwards do morph operators, as that way discontinuities should be 1px. I just thought that someone could give me some advice in that direction. – zetah Feb 20 '13 at 10:27
ahh ok. can you put up an example image? – geometrikal Feb 22 '13 at 13:01

This is by no means a complete answer but I think it will get you far enough to achieve what you want. Edit: maybe not.

• find main colours using kmeans
• assign main colours to different types (brown line, blue line, etc). Some appear in both
• clean up with morphology
• segment image

What I thought might work was to pick out the most common colours and classify each pixel by the nearest colour. Then classify each pixel by type (brown line, etc), where the colour class can belong to multiple type classes. This would hopefully solve the problem of gaps in the lines - i.e. the colour where blue crosses brown would belong to both type class and fill the gap.

Not sure if it really does a better job. The issue with kmeans is that it uses random starting points so the colour classes were changing every time the script was run. I think I've fixed that now.

I wonder if there is some code out there to connect the broken lines?

% Reshape into 2D matrix of pixel colours
ir = reshape(I, [size(I,1) * size(I,2),3]);

% Choose exact same sample points everytime
rng(0);
ir_indx = randi(size(ir,1),20,1);
ir_startpoints = ir(ir_indx,:);

% Use k-means clustering to find most common colours
[ik,c,sk,sd] = kmeans(double(ir),20,'Start',double(ir_startpoints));

% Show colours
cim = reshape(uint8(c),[1,20,3]);
image(cim); title('Colours'); pause;

% Sort colours
chsv = rgb2hsv(c);
[~,cni] = sort(chsv(:,1),1);
cn = c(cni,:);
cnim = reshape(uint8(cn),[1,20,3]);
image(cnim); title('Sorted colours'); pause;

% Get index of index
[~,cni2] = sort(cni);

% Show pixels classified
in = reshape(cni2(ik,:), [size(I,1),size(I,2)]);
imagesc(in);  title('Pixel classification'); pause;

% Show each class
In = zeros(size(I));
for k = 1:20
In(:,:,1) = I(:,:,1) .* uint8(in == k);
In(:,:,2) = I(:,:,2) .* uint8(in == k);
In(:,:,3) = I(:,:,3) .* uint8(in == k);
image(uint8(In)); title (['Class ' num2str(k)]); pause;
im_dist(:,:,k) = reshape(sd(:,k), [size(I,1),size(I,2)]);
imagesc(-im_dist(:,:,k) .* (in == k));  title('Pixel distance'); pause;
end

% 12,19 and 20 blue lines
im_blue = (in == 13) | (in == 19) | (in == 20);
imagesc(im_blue); title('Blue lines'); colormap gray; pause;
im_blue = imopen(im_blue,strel('diamond',1));
imagesc(im_blue); title('Blue lines opened'); colormap gray; pause;
for k=1:3; In(:,:,k) = I(:,:,k) .* uint8(im_blue); end;
image(uint8(In)); pause;
imagesc(bwmorph(im_blue,'skel',Inf));

% brown lines
im_brown = (in == 1) | (in == 2) | (in == 1) | ...
(in == 7) | (in == 8) | (in == 11) | (in == 12) | (in == 13);
imagesc(im_brown); title('Brown lines'); pause;
im_brown = imopen(im_brown,strel('diamond',1));
imagesc(im_brown); title('Brown lines opened'); pause;
for k=1:3; In(:,:,k) = I(:,:,k) .* uint8(im_brown); end;
image(uint8(In)); pause;
imagesc(bwmorph(im_brown,'skel',Inf));

% 9 - black line
im_black = (in == 10);
imagesc(im_black); title('Black lines'); pause;
im_black = imopen(im_black,strel('diamond',1));
imagesc(im_black); title('Black lines opened'); pause
for k=1:3; In(:,:,k) = I(:,:,k) .* uint8(im_black); end;
image(uint8(In)); pause;
imagesc(bwmorph(im_black,'skel',Inf));

% 16,17,18 green area
im_green = (in == 16) | (in == 17) | (in == 18);
imagesc(im_green); title('Grean area'); pause;
im_green = imopen(im_green,strel('diamond',1));
imagesc(im_green); title('Green area opened'); pause;
im_green = imclose(im_green,strel('disk',7));
imagesc(im_green); title('Green area opened and closed'); pause;
for k=1:3; In(:,:,k) = I(:,:,k) .* uint8(im_green); end;
image(uint8(In)); pause;
-
Thanks for your effort geometrikal. I'm sorry but I just can't see what are you trying to suggest. If I'm not mistaken you show me here how to segment image based on colors. I can do that with any decent image processing tool. For reference and those interested in results of above code, topological (brown) lines are extracted as following image: i.imgur.com/fMPBBom.png I can do better than that, and I upvoted you answer – zetah Feb 28 '13 at 21:14
@zetah ahh I thought that might happen. The kmeans clustering gets slightly different results each time. I was in a rush yesterday and didnt post up my results, I will update shortly with some comments. – geometrikal Mar 1 '13 at 1:05
OK. I don't understand how you expect to find "crossing" color, as image lines are not blended but one line is drawn on top of other. About connecting broken lines there are actually several methods, but none is easy. For example: PDF. This apparently belongs to a topic of curve reconstruction, and contour closing is one of "easier" subtopics. The thing is, I could find such method implemented anywhere (not in a library nor even in GIS tools, like gdal or ArcGIS or QGIS) – zetah Mar 1 '13 at 3:31
Some of the colours are a bit blended (not all of them) thats why I looked for the crossing colours. I had a quick look as well and I couldn't find contour closing either. Sorry I can't be more help at the moment! Good luck! P.S. what is the application? Map processing? – geometrikal Mar 1 '13 at 4:58
OK, thanks for your time geometrikal. I need to reconstruct older map, while I'm not trained for the process. – zetah Mar 1 '13 at 5:13