machine learning – How to remove blurred background of images?
This particular problem isnt too difficult to solve. Its not as bad as it could be since theres only one foreground object which means we can solve it using simple methods. @Ceopees instinct of edge detection is the correct one since its the most obvious way that the foreground will differ from a blurry background.
I grayscaled the image and use a Canny edge detector. I didnt really tune it at all so we get a bunch of edges, but we only care about finding the edges of the leaves. Thankfully, we dont have to spend a bunch of time tuning it for each image since we only care about the biggest continuous edge.
I dilate the image to connect up nearby edges (canny gives 1 pixel wide lines which are easily disconnected) and then use findContours to get an outline of all of the white lines. I sort through and pick the biggest contour by area and use that one to create a mask.
The jaggedness of the mask bothers me so I do an opening operation (to cut off thin jaggies) followed by a median blur (to smooth out the edges).
Then all there is to do is use the mask to crop out the image and its done. (I had to change it to a jpg to make the 2mb limit so there might be some compression artifacts on here).
Heres the code (Note this is in OpenCV 3.4, if youre using a different major version then youll have to modify the findContours line)
import cv2 import numpy as np # load image img = cv2.imread(leaf.jpg); # grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY); # canny canned = cv2.Canny(gray, 0, 100); # dilate to close holes in lines kernel = np.ones((3,3),np.uint8) mask = cv2.dilate(canned, kernel, iterations = 1); # find contours # Opencv 3.4, if using a different major version (4.0 or 2.0), remove the first underscore _, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE); # find the biggest contour biggest_cntr = None; biggest_area = 0; for contour in contours: area = cv2.contourArea(contour); if area > biggest_area: biggest_area = area; biggest_cntr = contour; # draw contours crop_mask = np.zeros_like(mask); cv2.drawContours(crop_mask, [biggest_cntr], -1, (255), -1); # opening + median blur to smooth jaggies crop_mask = cv2.erode(crop_mask, kernel, iterations = 5); crop_mask = cv2.dilate(crop_mask, kernel, iterations = 5); crop_mask = cv2.medianBlur(crop_mask, 21); # crop image crop = np.zeros_like(img); crop[crop_mask == 255] = img[crop_mask == 255]; # show cv2.imshow(leaf, img); cv2.imshow(gray, gray); cv2.imshow(canny, canned); cv2.imshow(mask, crop_mask); cv2.imshow(cropped, crop); cv2.waitKey(0);
If you want to generalize this to include multiple foreground objects you could filter the contours by size and reject contours smaller than a certain threshold.