Rectangles overlapping - python

I am using opencv to draw rectangles over images with the xmin, ymin, xmax, ymax value of the rectangles given in a list.
List of point is
points = [(1707.0, 1865.0, 2331.0, 2549.0),(1348.0, 1004.0, 1987.0, 1746.0),(749.0, 2129.0, 1674.0, 2939.0)
,(25.0, 1134.0, 1266.0, 2108.0),(253.0, 1731.0, 1403.0, 2449.0)]
image = cv2.imread("pathtoimage")
for point in points:
xmin,ymin,xmax,ymax = point
result_image = cv2.rectangle(image, (int(xmin), int(xmax)), (int(ymin),int(ymax)), (0,255,0), 8)
os.remove("/home/atul/Documents/CarLabel/imagemapping1-wp-BD489663-BD55-484E-9EA7-EB5662B626B9.png")
cv2.imwrite("/home/atul/Documents/CarLabel/imagemapping1-wp-BD489663-BD55-484E-9EA7-EB5662B626B9.png",result_image)
Rectangles are getting overlapped into each other.
How can I resolve this.
Original Image
Resulting image

cv2.rectangle needs the coordintates of top-left and bottom-right points. So you should use:
result_image = cv2.rectangle(image, (int(xmin), int(ymin)), (int(xmax),int(ymax)), (0,255,0), 8)

Related

How can I detect object which in specific area with tensorflow?

I am trying to detect some objects with Tensorflow. However, I only want to detect objects in a specific area in the main image. For example:
Here my example area:
The large red framed area is my main image. But I only want to detect objects which in the "content" field.
enter image description here
Actually, I developed a method like this. I find the center point of the detected object and check if the center point is within this area. If the center of the object is within the specified region, tensorflow takes the detected object into a rectangle. This method works when I'm going to detect a single object. However, I am faced with the following situation: For example, one object is inside the desired region and another object is outside the desired region. Tensorflow actually detects these two objects and adds them to a list. When Tensorflow wants to show the object in the desired region, it necessarily frames all the detected objects in the list. In short, when a single object is detected, objects outside the marked area are also detected and added to the list, but they are ignored and a rectangle is not drawn. However, if an object is detected within the marked area, this time a rectangle is drawn on the object outside the marked area. How do I prevent this?
There is a part for draw boxes:
vis_util.visualize_boxes_and_labels_on_image_array(
image_np,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
category_index,
use_normalized_coordinates=True,
line_thickness=3,
min_score_thresh=0.2)
In this part, I actually send all detected objects to the function because I don't know which object is the correct one in the list.
And here the visualize_boxes_and_labels_on_image_array function:
def visualize_boxes_and_labels_on_image_array(
image,
boxes,
classes,
scores,
category_index,
instance_masks=None,
instance_boundaries=None,
keypoints=None,
use_normalized_coordinates=False,
max_boxes_to_draw=2,
min_score_thresh=.5,
agnostic_mode=False,
line_thickness=4,
groundtruth_box_visualization_color='black',
skip_scores=False,
skip_labels=False):
"""Overlay labeled boxes on an image with formatted scores and label names.
This function groups boxes that correspond to the same location
and creates a display string for each detection and overlays these
on the image. Note that this function modifies the image in place, and returns
that same image.
Args:
image: uint8 numpy array with shape (img_height, img_width, 3)
boxes: a numpy array of shape [N, 4]
classes: a numpy array of shape [N]. Note that class indices are 1-based,
and match the keys in the label map.
scores: a numpy array of shape [N] or None. If scores=None, then
this function assumes that the boxes to be plotted are groundtruth
boxes and plot all boxes as black with no classes or scores.
category_index: a dict containing category dictionaries (each holding
category index `id` and category name `name`) keyed by category indices.
instance_masks: a numpy array of shape [N, image_height, image_width] with
values ranging between 0 and 1, can be None.
instance_boundaries: a numpy array of shape [N, image_height, image_width]
with values ranging between 0 and 1, can be None.
keypoints: a numpy array of shape [N, num_keypoints, 2], can
be None
use_normalized_coordinates: whether boxes is to be interpreted as
normalized coordinates or not.
max_boxes_to_draw: maximum number of boxes to visualize. If None, draw
all boxes.
min_score_thresh: minimum score threshold for a box to be visualized
agnostic_mode: boolean (default: False) controlling whether to evaluate in
class-agnostic mode or not. This mode will display scores but ignore
classes.
line_thickness: integer (default: 4) controlling line width of the boxes.
groundtruth_box_visualization_color: box color for visualizing groundtruth
boxes
skip_scores: whether to skip score when drawing a single detection
skip_labels: whether to skip label when drawing a single detection
Returns:
uint8 numpy array with shape (img_height, img_width, 3) with overlaid boxes.
"""
# Create a display string (and color) for every box location, group any boxes
# that correspond to the same location.
box_to_display_str_map = collections.defaultdict(list)
box_to_color_map = collections.defaultdict(str)
box_to_instance_masks_map = {}
box_to_instance_boundaries_map = {}
box_to_keypoints_map = collections.defaultdict(list)
if not max_boxes_to_draw:
max_boxes_to_draw = boxes.shape[0]
for i in range(min(max_boxes_to_draw, boxes.shape[0])):
if scores is None or scores[i] > min_score_thresh:
box = tuple(boxes[i].tolist())
if instance_masks is not None:
box_to_instance_masks_map[box] = instance_masks[i]
if instance_boundaries is not None:
box_to_instance_boundaries_map[box] = instance_boundaries[i]
if keypoints is not None:
box_to_keypoints_map[box].extend(keypoints[i])
if scores is None:
box_to_color_map[box] = groundtruth_box_visualization_color
else:
display_str = ''
if not skip_labels:
if not agnostic_mode:
if classes[i] in category_index.keys():
class_name = category_index[classes[i]]['name']
else:
class_name = 'N/A'
display_str = str(class_name)
if not skip_scores:
if not display_str:
display_str = '{}%'.format(int(100*scores[i]))
else:
display_str = '{}: {}%'.format(display_str, int(100*scores[i]))
box_to_display_str_map[box].append(display_str)
if agnostic_mode:
box_to_color_map[box] = 'DarkOrange'
else:
box_to_color_map[box] = STANDARD_COLORS[
classes[i] % len(STANDARD_COLORS)]
# Draw all boxes onto image.
for box, color in box_to_color_map.items():
ymin, xmin, ymax, xmax = box
if instance_masks is not None:
draw_mask_on_image_array(
image,
box_to_instance_masks_map[box],
color=color
)
if instance_boundaries is not None:
draw_mask_on_image_array(
image,
box_to_instance_boundaries_map[box],
color='red',
alpha=1.0
)
draw_bounding_box_on_image_array(
image,
ymin,
xmin,
ymax,
xmax,
color=color,
thickness=line_thickness,
display_str_list=box_to_display_str_map[box],
use_normalized_coordinates=use_normalized_coordinates)
if keypoints is not None:
draw_keypoints_on_image_array(
image,
box_to_keypoints_map[box],
color=color,
radius=line_thickness / 2,
use_normalized_coordinates=use_normalized_coordinates)
return image
In the above function the name "draw_bounding_box_on_image_array" calls another function.
def draw_bounding_box_on_image_array(image,
ymin,
xmin,
ymax,
xmax,
color='red',
thickness=4,
display_str_list=(),
use_normalized_coordinates=True):
"""Adds a bounding box to an image (numpy array).
Bounding box coordinates can be specified in either absolute (pixel) or
normalized coordinates by setting the use_normalized_coordinates argument.
Args:
image: a numpy array with shape [height, width, 3].
ymin: ymin of bounding box.
xmin: xmin of bounding box.
ymax: ymax of bounding box.
xmax: xmax of bounding box.
color: color to draw bounding box. Default is red.
thickness: line thickness. Default value is 4.
display_str_list: list of strings to display in box
(each to be shown on its own line).
use_normalized_coordinates: If True (default), treat coordinates
ymin, xmin, ymax, xmax as relative to the image. Otherwise treat
coordinates as absolute.
"""
image_pil = Image.fromarray(np.uint8(image)).convert('RGB')
draw_bounding_box_on_image(image_pil, ymin, xmin, ymax, xmax, color,
thickness, display_str_list,
use_normalized_coordinates)
np.copyto(image, np.array(image_pil))
Finally, this function calls another function called "draw_bounding_box_on_image".
def draw_bounding_box_on_image(image,
ymin,
xmin,
ymax,
xmax,
color='red',
thickness=4,
display_str_list=(),
use_normalized_coordinates=True):
"""Adds a bounding box to an image.
Bounding box coordinates can be specified in either absolute (pixel) or
normalized coordinates by setting the use_normalized_coordinates argument.
Each string in display_str_list is displayed on a separate line above the
bounding box in black text on a rectangle filled with the input 'color'.
If the top of the bounding box extends to the edge of the image, the strings
are displayed below the bounding box.
Args:
image: a PIL.Image object.
ymin: ymin of bounding box.
xmin: xmin of bounding box.
ymax: ymax of bounding box.
xmax: xmax of bounding box.
color: color to draw bounding box. Default is red.
thickness: line thickness. Default value is 4.
display_str_list: list of strings to display in box
(each to be shown on its own line).
use_normalized_coordinates: If True (default), treat coordinates
ymin, xmin, ymax, xmax as relative to the image. Otherwise treat
coordinates as absolute.
"""
draw = ImageDraw.Draw(image)
im_width, im_height = image.size
if use_normalized_coordinates:
(left, right, top, bottom) = (xmin * im_width, xmax * im_width,
ymin * im_height, ymax * im_height)
else:
(left, right, top, bottom) = (xmin, xmax, ymin, ymax)
draw.line([(left, top), (left, bottom), (right, bottom),
(right, top), (left, top)], width=thickness, fill=color)
try:
font = ImageFont.truetype('arial.ttf', 24)
except IOError:
font = ImageFont.load_default()
# If the total height of the display strings added to the top of the bounding
# box exceeds the top of the image, stack the strings below the bounding box
# instead of above.
display_str_heights = [font.getsize(ds)[1] for ds in display_str_list]
# Each display_str has a top and bottom margin of 0.05x.
total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)
if top > total_display_str_height:
text_bottom = top
else:
text_bottom = bottom + total_display_str_height
# Reverse list and print from bottom to top.
for display_str in display_str_list[::-1]:
text_width, text_height = font.getsize(display_str)
margin = np.ceil(0.05 * text_height)
draw.rectangle(
[(left, text_bottom - text_height - 2 * margin), (left + text_width,
text_bottom)],
fill=color)
draw.text(
(left + margin, text_bottom - text_height - margin),
display_str,
fill='black',
font=font)
text_bottom -= text_height - 2 * margin
As far as I understand, this function draws a rectangle using the coordinates of the detected object. Can you please help with this issue?

Python - openCv to detect circles giving strange results

I'm using the code explained in https://www.pyimagesearch.com/2014/07/21/detecting-circles-images-using-opencv-hough-circles/#comment-480634 and trying to basically detect the small rounded profile images (to be precise 5) displayed lower half of this sample instagram page (attached). What I can't figure out is why:
1. only one out of the 5 small rounded profile circles is captured by the code
2. how come there's a big circle displayed on the page and seems quite absurd to me.
Here is the code I'm using:
# we create a copy of the original image so we can draw our detected circles
# without destroying the original image.
image = cv2.imread("instagram_page.png")
# the cv2.HoughCircles function requires an 8-bit, single channel image,
# so we’ll convert from the RGB color space to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# detect circles in the image. We pass in the image we want to detect circles as the first argument,
# the circle detection method as the second argument (currently, the cv2.cv.HOUGH_GRADIENT method
# is the only circle detection method supported by OpenCV and will likely be the only method for some time),
# an accumulator value of 1.5 as the third argument, and finally a minDist of 100 pixels.
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.7, minDist= 1, param1 = 300, param2 = 100, minRadius=3, maxRadius=150)
print("Circles len -> {}".format(len(circles)))
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
# converting our circles from floating point (x, y) coordinates to integers,
# allowing us to draw them on our output image.
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
orange = (39, 127, 255)
cv2.circle(output, (x, y), r, orange, 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
img_name = "Output"
cv2.namedWindow(img_name,cv2.WINDOW_NORMAL)
cv2.resizeWindow(img_name, 800,800)
cv2.imshow(img_name, output)
cv2.waitKey(0)
cv2.destroyAllWindows()
I use minDist = 1 to make sure those close circles are potentially captured. Does anybody see something completely wrong with my parameters?
I played around with the parameters and managed to detect all circles (Ubuntu 16.04 LTS x64, Python 3.7, numpy==1.15.1, python-opencv==3.4.3):
circles = cv2.HoughCircles(
gray,
cv2.HOUGH_GRADIENT,
1.7,
minDist=100,
param1=48,
param2=100,
minRadius=2,
maxRadius=100
)

Drawing true filled circle on an image

I have an image which I define like the following:
img = np.zeros(474,474)
I would like to draw true filled circles and not polygonal approximations of circles on this image at different coordinates as centre and of a fixed radius. For example, I want to draw two circles with centres (100,200) and (150,372) with radius 2 pixels. What I am expecting is that after plotting the circles, the entries of the original image img should change to all ones where the circle is present.
I tried opencv cv.circlemodule as well as skimage.draw.circle module but they generate some polynomial approximation of circle.
I was also trying the following in matplotlib but I don't seem to understand how to plot it on my image img.
Any help would be appreciated.
from matplotlib.patches import Circle
img=np.zeros(474,474)
fig = plt.figure()
ax = fig.add_subplot(111)
centers = [(100,200),(150,372)]
for i in range(len(centers)):
Circle((centers[i][0],centers[i][1]), radius= 2)
draw circles in the img.
import cv2
import numpy as np
img = np.zeros([474, 474])
cv2.circle(img, (100,100), 5, 255, -1)
cv2.circle(img, (200,200), 30, 255, -1)
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
When drawing a circle in OpenCV, there are several parameters that can be chosen, one of them is used to define the type of the circle boundary, you have 4 types:
Filled
4-connected line
8-connected line
antialiased line
You can see the different effect on the following image (in same order)
An example code (in C++)
circle(src,cv::Point(300,300), 10, Scalar(0,0,255), 1, FILLED);

How can I number circles in a certain order using python?

I want to get the shade value of each circles from an image.
I try to detect circles using HoughCircle.
I get the center of each circle.
I put the text (the circle numbers) in a circle.
I set the pixel subset to obtain the shading values and calculate the averaged shading values.
I want to get the results of circle number, the coordinates of the center, and averaged shading values in CSV format.
But, in the 3rd step, the circle numbers were randomly assigned. So, it's so hard to find circle number.
How can I number circles in a sequence?
# USAGE
# python detect_circles.py --image images/simple.png
# import the necessary packages
import numpy as np
import argparse
import cv2
import csv
# define a funtion of ROI calculating the average value in specified sample size
def ROI(img,x,y,sample_size):
Each_circle=img[y-sample_size:y+sample_size, x-sample_size:x+sample_size]
average_values=np.mean(Each_circle)
return average_values
# open the csv file named circles_value
circles_values=open('circles_value.csv', 'w')
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread(args["image"])
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2,50, 100, 1, 1, 20, 30)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
number=1
font = cv2.FONT_HERSHEY_SIMPLEX
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
number=str(number)
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 10, y - 10), (x + 10, y + 10), (0, 128, 255), -1)
# number each circle, but its result shows irregular pattern
cv2.putText(output, number, (x,y), font,0.5,(0,0,0),2,cv2.LINE_AA)
# get the average value in specified sample size (20 x 20)
sample_average_value=ROI(output, x, y, 20)
# write the csv file with number, (x,y), and average pixel value
circles_values.write(number+','+str(x)+','+str(y)+','+str(sample_average_value)+'\n')
number=int(number)
number+=1
# show the output image
cv2.namedWindow("image", cv2.WINDOW_NORMAL)
cv2.imshow("image", output)
cv2.waitKey(0)
# close the csv file
circles_values.close()
You could sort your circles based on their x, y values, the width of the image and a rough line height, for example:
import numpy as np
import argparse
import cv2
import csv
# define a funtion of ROI calculating the average value in specified sample size
def ROI(img,x,y,sample_size):
Each_circle=img[y-sample_size:y+sample_size, x-sample_size:x+sample_size]
average_values=np.mean(Each_circle)
return average_values
# open the csv file named circles_value
with open('circles_value.csv', 'wb') as circles_values:
csv_output = csv.writer(circles_values)
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True, help = "Path to the image")
args = vars(ap.parse_args())
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread(args["image"])
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1.2,50, 100, 1, 1, 20, 30)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
font = cv2.FONT_HERSHEY_SIMPLEX
height = 40
# loop over the (x, y) coordinates and radius of the circles
for number, (x, y, r) in enumerate(sorted(circles, key=lambda v: v[0] + (v[1] / height) * image.shape[1]), start=1):
text = str(number)
(tw, th), bl = cv2.getTextSize(text, font, 0.5, 2) # So the text can be centred in the circle
tw /= 2
th = th / 2 + 2
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(output, (x, y), r, (0, 255, 0), 3)
cv2.rectangle(output, (x - tw, y - th), (x + tw, y + th), (0, 128, 255), -1)
# number each circle, centred in the rectangle
cv2.putText(output, text, (x-tw, y + bl), font, 0.5, (0,0,0), 2, cv2.CV_AA)
# get the average value in specified sample size (20 x 20)
sample_average_value = ROI(output, x, y, 20)
# write the csv file with number, (x,y), and average pixel value
csv_output.writerow([number, x, y, sample_average_value])
# show the output image
cv2.namedWindow("image", cv2.WINDOW_NORMAL)
cv2.imshow("image", output)
cv2.waitKey(0)
Also, it is easier to use Python's CSV library to write entries to your output file. This way you don't need to convert each entry to a string and add commas between each entry. enumerate() can be used to count each circle automatically. Also getTextSize() can be used to determine the dimensions of the text to be printed enabling you to centre it in the rectangle.
This would give you an output as follows:
And a CSV starting as:
1,2,29,nan
2,51,19,nan
3,107,22,100.72437499999999
4,173,23,102.33291666666666
5,233,26,88.244791666666671
6,295,22,92.953541666666666
7,358,28,142.51625000000001
8,418,26,155.12875
9,484,31,127.02541666666667
10,547,25,112.57958333333333
The mistake in your code is that your number is dependent upon the order of circles in list returned from cv2.HoughCircles which can be random, So what I would have done in this situation is to devise a formula which would convert the center(x, y) value of each circle to an ID, and the same circle would yield same ID given its center position remains same:
def get_id_from_center(x, y):
return x + y*50
for (x, y, r) in circles:
number = str(get_id_from_center(x, y))

Compute the centroid of a rectangle in python

I want to compute the centroid of rectangle, The coordinates of rectangle are as follows:
co_ord = (601, 1006,604, 1009) ## (xmin,ymin,xmax,ymax)
Can someone point to an easy way.
Thanks
The centroid of a rectangle with opposite corners (x1, y1) and (x2, y2) lies at the center of that rectangle ((x1+x2)/2, (y1+y2)/2)
First, I am assuming that by saying centroid, you mean center. Next, I assume that the coord tuple is in the format: (x, y, width, height). In that case, it would be done this way:
coord = (601, 1006, 604, 1009)
centerCoord = (coord[0]+(coord[2]/2), coord[1]+(coord[3]/2))
where centerCoord would be the coordinates of the center in the format (x, y).
If you have the right coord of the rectangle, you can easily compute the centroid point coordinates with a formula:
If you have the 2 opposite points of the rectangle, you can use this:
Point A: X1; Y1
Point B: X2; Y2
Computed centroid points:
Coord X: (x1+x2)/2
Coord Y: (y1+y2)/2
Just a suggestion:
You can write a checking part in your program. You should check the parameters which your program get. It's not required for the basic running but it would be better if the program checks the rectangle is a real rectangle.
I think this should brighten things up for you. I will use the openCV library to make the example more understandable.
To find a center you need to coordinates: x and y.
# Draw a rectangle on the image. pt1 = left upper corner, pt2 = right bottom corner
cv2.rectangle(img=your_image, pt1=(px1_width, px2_height), pt2=(px3_width, px4_height), color=(0, 255, 0), thickness=3)
# Calculate the center point of rectangle
x, y = (px1_width + px3_width) // 2, (px2_height + px4_height) // 2
# Draw a circle in the center of rectangle
cv2.circle(img=your_image, center=(x, y), radius=3, color=(0, 255, 0), thickness=3)

Categories