My problem statement is segment the collaged images into individual images by applying a bounding box. I've approached this using opencv's canny edge detection method:
Code:
import numpy as np
import os
import cv2
import matplotlib.pyplot as plt
# defining the canny detector function
# here weak_th and strong_th are thresholds for
# double thresholding step
def Canny_detector(img, weak_th = None, strong_th = None):
# conversion of image to grayscale
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Noise reduction step
img = cv2.GaussianBlur(img, (5, 5), 1.4)
# Calculating the gradients
gx = cv2.Sobel(np.float32(img), cv2.CV_64F, 1, 0, 3)
gy = cv2.Sobel(np.float32(img), cv2.CV_64F, 0, 1, 3)
# Conversion of Cartesian coordinates to polar
mag, ang = cv2.cartToPolar(gx, gy, angleInDegrees = True)
# setting the minimum and maximum thresholds
# for double thresholding
mag_max = np.max(mag)
if not weak_th:weak_th = mag_max * 0.1
if not strong_th:strong_th = mag_max * 0.5
# getting the dimensions of the input image
height, width = img.shape
# Looping through every pixel of the grayscale
# image
for i_x in range(width):
for i_y in range(height):
grad_ang = ang[i_y, i_x]
grad_ang = abs(grad_ang-180) if abs(grad_ang)>180 else abs(grad_ang)
# selecting the neighbours of the target pixel
# according to the gradient direction
# In the x axis direction
if grad_ang<= 22.5:
neighb_1_x, neighb_1_y = i_x-1, i_y
neighb_2_x, neighb_2_y = i_x + 1, i_y
# top right (diagnol-1) direction
elif grad_ang>22.5 and grad_ang<=(22.5 + 45):
neighb_1_x, neighb_1_y = i_x-1, i_y-1
neighb_2_x, neighb_2_y = i_x + 1, i_y + 1
# In y-axis direction
elif grad_ang>(22.5 + 45) and grad_ang<=(22.5 + 90):
neighb_1_x, neighb_1_y = i_x, i_y-1
neighb_2_x, neighb_2_y = i_x, i_y + 1
# top left (diagnol-2) direction
elif grad_ang>(22.5 + 90) and grad_ang<=(22.5 + 135):
neighb_1_x, neighb_1_y = i_x-1, i_y + 1
neighb_2_x, neighb_2_y = i_x + 1, i_y-1
# Now it restarts the cycle
elif grad_ang>(22.5 + 135) and grad_ang<=(22.5 + 180):
neighb_1_x, neighb_1_y = i_x-1, i_y
neighb_2_x, neighb_2_y = i_x + 1, i_y
# Non-maximum suppression step
if width>neighb_1_x>= 0 and height>neighb_1_y>= 0:
if mag[i_y, i_x]<mag[neighb_1_y, neighb_1_x]:
mag[i_y, i_x]= 0
continue
if width>neighb_2_x>= 0 and height>neighb_2_y>= 0:
if mag[i_y, i_x]<mag[neighb_2_y, neighb_2_x]:
mag[i_y, i_x]= 0
weak_ids = np.zeros_like(img)
strong_ids = np.zeros_like(img)
ids = np.zeros_like(img)
# double thresholding step
for i_x in range(width):
for i_y in range(height):
grad_mag = mag[i_y, i_x]
if grad_mag<weak_th:
mag[i_y, i_x]= 0
elif strong_th>grad_mag>= weak_th:
ids[i_y, i_x]= 1
else:
ids[i_y, i_x]= 2
# finally returning the magnitude of
# gradients of edges
return mag
frame = cv2.imread(r"image.jpg")
# calling the designed function for
# finding edges
canny_img = Canny_detector(frame)
With this I'm getting a canny edge detected image as below:
Now I want to segment the collaged image using the outer edges. How to achieve the expected result?
Expected Result:
Here is one way to do that in Python OpenCV.
- Read the input
- Convert to gray
- Threshold and invert
- Apply morphology open and then close to clean up most of the extraneous spots and make cleaner boundaries
- Get the external contours
- Loop over each contour and get the bounding boxes, crop and save the results
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('rooms.jpg')
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold on white
thresh = cv2.threshold(gray, 245, 255, cv2.THRESH_BINARY)[1]
# invert
thresh = 255 - thresh
# apply morphology to ensure regions are filled and remove extraneous noise
kernel = np.ones((3,3), np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = np.ones((15,15), np.uint8)
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# get only external contours
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# get bounding boxes
i = 1
for cntr in contours:
# get bounding boxes
x,y,w,h = cv2.boundingRect(cntr)
print(i,x,y,w,h)
crop = img[y:y+h, x:x+w]
cv2.imwrite("rooms_crop_{0}.jpg".format(i), crop)
i = i + 1
# save threshold and morph
cv2.imwrite("rooms_thresh.jpg",thresh)
cv2.imwrite("rooms_morph.jpg",thresh)
# show thresh and morph
cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold image:
Morphology cleaned image:
Cropped Images: