Extract line shaped objects


I'm working on images with overlapping line shapes (left plot). Ultimately I want to segment single objects. I'm working with a Hough transform to achieve this and it works well in finding lines of (significantly) different orientation - e.g. represented by the two maxima in the hough space below (middle plot).

houghline - full example

  • the green and yellow lines (left plot) and crosses (right plot) stem from an approach to do something with the thickness of the line. I couldn't figure out how to extract a broad line though, so I didn't follow up.
  • I'm aware of the ambiguity of assigning the "overlapping pixels". I will address that later.

Since I don't know, how many line objects one connected region may contain, my idea is to iteratively extract the object corresponding to the hough line with the highest activation (here painted in blue), i.e. remove the line shaped object from the image, so that the next iteration will find only the other line.

But how do I detect, which pixels belong to the line shaped object?

The function hough_bin_pixels(img, theta, rho, P) (from here - shown in the right plot) gives pixels corresponding to the particular line. But that obviously is too thin of a line to represent the object.

Is there a way to segment/detect the whole object that is orientied along the strongest houghline?


Answers:


The key is knowing that thick lines in the original image translate to wider peaks on the Hough Transform. This image shows the peaks of a thin and a thick line.

Thick and thin line with resulting Hough Transform

You can use any strategy you like to group all the pixels/accumulator bins of each peak together. I would recommend using multithresh and imquantize to convert it to a BW image, and then bwlabel to label the connected components. You could also use any number of other clustering/segmentation strategies. The only potentially tricky part is figuring out the appropriate thresholding levels. If you can't get anything suitable for your application, err on the side of including too much because you can always get rid of erroneous pixels later.

Here are the peaks of the Hough Transform after thresholding (left) and labeling (right)

Thresholded peaksLabeled peaks

Once you have the peak regions, you can find out which pixels in the original image contributed to each accumulator bin using hough_bin_pixels. Then, for each peak region, combine the results of hough_bin_pixels for every bin that is part of the region.

Reconstructed Lines

Here is the code I threw together to create the sample images. I'm just getting back into matlab after not using it for a while, so please forgive the sloppy code.

% Create an image
image = zeros(100,100);

for i = 10:90
    image(100-i,i)=1;
end;

image(10:90, 30:35) = 1;

figure, imshow(image); % Fig. 1 -- Original Image

% Hough Transform
[H, theta_vals, rho_vals] = hough(image);
figure, imshow(mat2gray(H)); % Fig. 2 -- Hough Transform

% Thresholding
thresh = multithresh(H,4);
q_image = imquantize(H, thresh);
q_image(q_image < 4) = 0;
q_image(q_image > 0) = 1;
figure, imshow(q_image) % Fig. 3 -- Thresholded Peaks

% Label connected components
L = bwlabel(q_image);
figure, imshow(label2rgb(L, prism)) % Fig. 4 -- Labeled peaks

% Reconstruct the lines
[r, c] = find(L(:,:)==1);
segmented_im = hough_bin_pixels(image, theta_vals, rho_vals, [r(1) c(1)]);
for i = 1:size(r(:))
    seg_part = hough_bin_pixels(image, theta_vals, rho_vals, [r(i) c(i)]);
    segmented_im(seg_part==1) = 1;
end
region1 = segmented_im;

[r, c] = find(L(:,:)==2);
segmented_im = hough_bin_pixels(image, theta_vals, rho_vals, [r(1) c(1)]);
for i = 1:size(r(:))
    seg_part = hough_bin_pixels(image, theta_vals, rho_vals, [r(i) c(i)]);
    segmented_im(seg_part==1) = 1;
end
region2 = segmented_im;

figure, imshow([region1 ones(100, 1) region2]) % Fig. 5 -- Segmented lines

% Overlay and display
out = cat(3, image, region1, region2);
figure, imshow(out); % Fig. 6 -- For fun, both regions overlaid on original image