4

I have an ROI and an image. I have to fill the ROI with the image that I have. The image should scale according to the ROI shape and size and should fill the entire ROI without repeating the image. How can I achieve this using opencv? Is there any method in opencv to achieve this?

Suppose this white section is my ROI and

suppose this is the ROI

this is my input image enter image description here

Is there any solution using imageMagick???

Rosa Gronchi
  • 1,828
  • 15
  • 25
Neeraj
  • 1,612
  • 7
  • 29
  • 47

1 Answers1

2

Finding optimal fit of one shape inside another is not trivial, but if you can settle for suboptimal result you can do the following:

import cv2
import numpy as np
from matplotlib import pyplot as plt

bg_contours, bg_hierarchy = cv2.findContours(bg_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
bg_contour = bg_contours[0]
bg_ellipse = cv2.fitEllipse(bg_contour)

p_contours, p_hierarchy = cv2.findContours(fruit_alpha, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

pear_hull = cv2.convexHull(p_contours[0])
pear_ellipse = cv2.fitEllipse(pear_hull)

min_ratio = min(bg_ellipse[1][0] / pear_ellipse[1][0], bg_ellipse[1][1] / pear_ellipse[1][1])

x_shift = bg_ellipse[0][0] - pear_ellipse[0][0] * min_ratio
y_shift = bg_ellipse[0][1] - pear_ellipse[0][1] * min_ratio

(Heuristic) Resize the fruit contour, start with an initial guess based on the ellipses, refine using the contour (this can be improved but it is a non trivial optimization problem, you can look more here):

r_contour = np.array([[[int(j) for j in i[0]]] for i in min_ratio * p_contours[max_c_ix]])

min_dist, bad_pt = GetMinDist(outer_contour=bg_contour, inner_contour=r_contour, offset=(int(x_shift), int(y_shift)))
mask_size = max(bg_ellipse[1][0], bg_ellipse[1][1])
scale = min_ratio * (mask_size + min_dist) / mask_size

r_contour = np.array([[[int(j) for j in i[0]]] for i in scale * p_contours[max_c_ix]])

Combine the images using the alpha channel:

combined = CombineImages(bg, fruit_rgb, fruit_alpha, scale, (int(x_shift), int(y_shift)))

Utility functions:

def GetMinDist(outer_contour, inner_contour, offset):
    min_dist = 10000
    bad_pt = (0,0)
    for i_pt in inner_contour:
        #pt = (float(i_pt[0][0]), float(i_pt[0][1]))
        pt = (i_pt[0][0] + int(offset[0]), i_pt[0][1] + int(offset[1]))
        dst = cv2.pointPolygonTest(outer_contour, pt, True)
        if dst < min_dist:
            min_dist = dst
            bad_pt = pt
    return min_dist, bad_pt

def CombineImages(mask_img, fruit_img, fruit_alpha, scale, offset):
    mask_height, mask_width, mask_dim = mask_img.shape
    combined_img = np.copy(mask_img)
    resized_fruit = np.copy(mask_img)
    resized_fruit[:] = 0
    resized_alpha = np.zeros( (mask_height, mask_width), fruit_alpha.dtype)
    f_height, f_width, f_dim = fruit_img.shape
    r_fruit = cv2.resize(fruit_img, (int(f_width*scale), int(f_height*scale)) )
    r_alpha = cv2.resize(fruit_alpha, (int(f_width*scale), int(f_height*scale)) )
    height, width, channels = r_fruit.shape
    roi_x_from = offset[0]
    roi_x_to   = offset[0] + width
    roi_y_from = offset[1]
    roi_y_to   = offset[1] + height
    resized_fruit[roi_y_from:roi_y_to, roi_x_from:roi_x_to, :] = r_fruit
    resized_alpha[roi_y_from:roi_y_to, roi_x_from:roi_x_to] = r_alpha
    for y in range(0,mask_height):
        for x in range(0, mask_width):
            if resized_alpha[y,x] > 0:
                combined_img[y,x,:] = resized_fruit[y,x,:]

    return combined_img

enter image description here

I Hope that helps.

(I omitted parts of the code that do not contribute to the understanding of the flow)

Rosa Gronchi
  • 1,828
  • 15
  • 25