Created
March 23, 2024 19:00
-
-
Save kevinlinxc/ed8fc8fc3aa4b4ded9ff128ef6df464b to your computer and use it in GitHub Desktop.
On key press, display a letter in the center of cv2 imshow, and get points on the contour of the letter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from PIL import Image, ImageDraw, ImageFont | |
import cv2 | |
import numpy as np | |
def get_bounding_rect_area(contour): | |
# calculate area using the minimum bounding rectangle instead of whatever cv2.contourArea does | |
rect = cv2.minAreaRect(contour) | |
width = rect[1][0] | |
height = rect[1][1] | |
return width * height | |
def draw_biggest_external_contour(frame, | |
border_size=0, | |
border_color=(255, 255, 255), | |
min_distance=50, | |
canny_low=50, | |
canny_high=150, | |
dilate_kernel_size=10): | |
# Add a white border to the frame, so that a fully black frame will still have an outline | |
frame = cv2.copyMakeBorder(frame, border_size, border_size, border_size, border_size, cv2.BORDER_CONSTANT, value=border_color) | |
# Convert the frame to grayscale | |
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (dilate_kernel_size, dilate_kernel_size)) | |
morphed = cv2.morphologyEx(gray_frame, cv2.MORPH_CLOSE, kernel) | |
# dilate and erode to remove noise | |
# Run Canny edge detection | |
edges = cv2.Canny(morphed, canny_low, canny_high) | |
# Find contours | |
contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) # cv2.RETR_EXTERNAL for just external, | |
# Find the largest n contours | |
if len(contours) == 0: | |
return [], frame, 0 | |
main_contours = [] | |
# add all contours with area greater than 100 | |
for contour in contours: | |
if get_bounding_rect_area(contour) > 200: | |
main_contours.append(contour) | |
# num_contours = len(main_contours) | |
# Extract points along the contour that are farther than min_distance away from each other | |
contour_points = [] | |
for main_contour in main_contours: | |
for i in range(len(main_contour)): | |
point = main_contour[i][0] | |
# if i is 0 or if the the point is far enough from the previous point | |
if i == 0 or np.linalg.norm(np.array(point) - np.array(contour_points[-1])) > min_distance: | |
contour_points.append(tuple(point)) | |
# reduce duplicates | |
new_contour_points = [] | |
for i in range(len(contour_points)): | |
if i == 0 or all(np.linalg.norm(np.array(contour_points[i]) - np.array(point)) > min_distance/3 for point in new_contour_points): | |
new_contour_points.append(contour_points[i]) | |
contour_points = new_contour_points | |
# Draw circles on the frame at the extracted points | |
for point in contour_points: | |
cv2.circle(frame, point, 8, (0, 255, 0), -1) | |
return contour_points, frame | |
if __name__ == "__main__": | |
font_size = 500 # Increase the font size for a much bigger text | |
font_filepath = "Lato-Black.ttf" # download a font to use, put it in the same directory | |
text_color = (0, 0, 0, 255) # RGBA format, where the last value is alpha (transparency) | |
padding = 50 # Padding size in pixels | |
# Initialize the font and mask image | |
font = ImageFont.truetype(font_filepath, size=font_size) | |
text = "A" # Initial text | |
text_width, text_height = font.getsize(text) | |
padded_width = text_width + 2 * padding | |
padded_height = text_height + 2 * padding | |
mask_image = Image.new("RGBA", (padded_width, padded_height), (255, 255, 255, 255)) # Transparent background | |
mask_draw = ImageDraw.Draw(mask_image) | |
# Calculate the position to center the text horizontally | |
text_x = (padded_width - text_width) // 2 | |
text_y = 0 | |
mask_draw.text((text_x, text_y), text, font=font, fill=text_color) # Draw text with padding | |
img = np.array(mask_image) | |
contour_points, frame = draw_biggest_external_contour(img) | |
last_letter = ord('A') | |
cv2.imshow("test", frame) | |
key = cv2.waitKey(0) & 0xFF | |
while key != 27: # the ESC key | |
print(key) | |
if key != 255 and key != 1: # default if no key is pressed, also 1 sometimes shows up for the first button? | |
text = chr(key) | |
# Clear the previous text in the mask image | |
mask_draw.rectangle((0, 0, padded_width, padded_height), fill=(255, 255, 255, 255)) | |
# Calculate the position to center the new text horizontally | |
text_width, text_height = font.getsize(text) | |
text_x = (padded_width - text_width) // 2 | |
text_y = 0 | |
# Draw the new text in the mask image with padding | |
mask_draw.text((text_x, text_y), text, font=font, fill=text_color) | |
# Convert the updated mask image to a numpy array for OpenCV processing | |
img = np.array(mask_image) | |
contour_points, frame = draw_biggest_external_contour(img) | |
# Display the updated image using OpenCV | |
cv2.imshow("test", frame) | |
last_letter = key | |
key = cv2.waitKeyEx(0) | |
cv2.destroyAllWindows() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment