1
votes

I have an image with a gentle gradient background and sharp foreground features that I want to detect. I wish to find green arcs in the image. However, the green arcs are only green relative to the background (they are semitransparent). The green arcs are also only one or two pixels wide.

As a result, for each pixel of the image, I want to subtract the average color of the surrounding pixels. The surrounding 5x5 or 10x10 pixels would be sufficient. This should leave the foreground features relatively untouched but remove the soft background, and I can then select the green channel for further processing.

Is there any way to do this efficiently? It doesn't have to be particularly accurate, but I want to run it at 30Hz on a 1080p image.

Image with soft background and sharp foreground

2
The traditional method would be to blur and then subtract. You may find that, if accuracy is not that important, you can scale the image efficiently down to 1/4 or less of its original size, and then use a smaller blur/averaging radius over a smaller image (which will be faster) then scale back up to the original size before subtracting.Mark Setchell
I have not benchmarked it or implemented it, but the Scale2x algorithm scale2x.it/algorithm looks interesting and was considered significant enough to include in CImg library.Mark Setchell
Not sure if you realise, but if you subtract the mean over the surrounding 9x9 area from each pixel, you get a distortion of the colours... thesetchells.com/a.pngMark Setchell
I did some benchmarking and on my machine with a 1920x1080 image, the box blur takes around 25ms and the subtraction takes around 15ms, yours will doubtless differ but maybe do the box blur in a separate thread and only do it every 10-30 frames frames unless the background is changing very quickly, then you will only have the 15ms subtraction times which may be ok on a 30fps video.Mark Setchell
The blurred subtraction works great, please make a proper answer so I can approve itjnnnnn

2 Answers

2
votes

You can achieve this by doing a simple box blur on the image and then subtracting that from the original image:

blur(img,blurred,Size(15,15));
diff=img-blurred;

I tried this on a 1920x1080 image and the blur took 25ms and the subtract took 15ms on a decent spec iMac.

If your background is not changing fast, you could calculate the blur over the space of a few frames in a second thread and keep re-using it till you recalculate it again a few frames later then you would only have 15ms subtraction to do for each of your 30fps rather than 45ms of processing.

1
votes

Depending on how smooth the background gradient is, edge detection followed by dilation might work well for you.

Edge detection will output the 3 lines that you've shown in the sample and discard the background gradient (again, depending on smoothness). Dilating the image then will thicken the edges so that your line contours are more significant.

You can superimpose this edge map on your original image as:

Mat src,edges;
//perform edge detection on src and output in edges. 
Mat result;
cvtColor(edges,edges,cv2.COLOR_GRAY2BGR);//change mask to a 3 channel image 
subtract(edges,src,result);
subtract(edges,result);

The result should contain only the 3 lines with 3 colours. From here on you can apply color filtering to select the green one.