[This is now solved. I've posted my updated code at the bottom of my question for reference].
I am trying to create a progam in Python to automatically overlay a small image onto a large image for a list of coordinates. I can get it to work for a single example of one small image onto a larger image, but not when I attempt to repeat this multiple times. I would be grateful if someone could please point out the error in my code (probably quite basic I hope, I'm not confident with Python).
The purpose of the code is to find the brightest points in an image (in this case an image of a starfield), use a threshold, erosion, dilation process to isolate the brightest/largest stars. The findContours function is then used, and a rectangle is drawn around each contour. The central coordinates of each rectangle is treated as the pixel coordinates of that star. I am attempting to then use those coordinates to overlay a smaller image onto the base image in each position where a bright star is detected. For some reason it isn't working, and I would be really grateful for some assistance, thank you.
No google search and stack search I've tried has turned up some code I could imitate unfortunately, and I haven't been able to successfully code this myself.
This is the code I used to overlay a single image onto a base image, which works properly:
import cv2
import numpy as np
fg_img = cv2.imread("image_small.png")
bg_img = cv2.imread("image_big.png")
cv2.imshow('small',fg_img)
cv2.imshow('big',bg_img)
h1, w1 = fg_img.shape[:2]
print (h1, w1)
pip_h = 10
pip_w = 10
bg_img[pip_h:pip_h+h1,pip_w:pip_w+w1] = fg_img
cv2.imshow('overlaid', bg_img)
cv2.waitKey(0)
And this is the code I'm trying to get to work:
import imutils
import cv2
fg_img = cv2.imread("image_small.png")
bg_img = cv2.imread("image_big.png")
graybg = cv2.cvtColor(bg_img, cv2.COLOR_BGR2GRAY)
h1, w1 = fg_img.shape[:2]
##print(h1, w1)
thresh = cv2.threshold(graybg, 225, 255, cv2.THRESH_BINARY)[1]
mask = thresh.copy()
mask = cv2.erode(mask, None, iterations=1)
mask2 = mask.copy()
mask2 = cv2.dilate(mask2, None, iterations = 2)
h2, w2 = mask2.shape[:2]
print(h2, w2)
cnts = cv2.findContours(mask2.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
output = mask2.copy()
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
print(x,y)
pip_h = y
pip_w = x
mask2[pip_h:pip_h+h1,pip_w:pip_w+w1] = fg_img
cv2.imshow("Contours", output)
cv2.waitKey(0)
The error message I receive when running the second program I reproduced above reads: "ValueError: could not broadcast input array from shape (82,70,3) into shape (11,70)". For reference, the 82x70 was the resolution of the smaller image, the large image was in fact 1920x1080 in resolution...
Thanks again for reading, I hope that the error is easily solvable. Thanks
CORRECTED CODE:
import imutils
import cv2
fg_img = cv2.imread("image_small.png")
bg_img = cv2.imread("image_big.png")
graybg = cv2.cvtColor(bg_img, cv2.COLOR_BGR2GRAY)
h1, w1 = fg_img.shape[:2]
print(h1, w1)
thresh = cv2.threshold(graybg, 225, 255, cv2.THRESH_BINARY)[1]
mask = thresh.copy()
mask = cv2.erode(mask, None, iterations=1)
mask2 = mask.copy()
mask2 = cv2.dilate(mask2, None, iterations = 2)
h2, w2 = mask2.shape[:2]
print(h2, w2)
cnts = cv2.findContours(mask2.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
pip_h = y
pip_w = x
print(pip_h, pip_w)
if h2 - pip_h > h1 + 1 and w2 - pip_w > w1 + 1:
bg_img[pip_h:pip_h+h1,pip_w:pip_w+w1] = fg_img
cv2.imshow("Contours", bg_img)
cv2.waitKey(0)
The problem with your code is that, inside the for loop:
x,y,w,h = cv2.boundingRect(c)
if pip_h=y
is close to the height of the original image h2
, then the slicing operation
mask2[pip_h:pip_h+h1]
will only give you h2 - pip_h
rows. As an extreme, think what happens when pip_h=h2
.
In short, the cloning operation
mask2[pip_h:pip_h+h1,pip_w:pip_w+w1] = fg_img
fails when h2 - pip_h < h1
or w2 - pip_w < w1
for the above reason.