Source code for improutils.visualisation.visualisation

import copy
import math

import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import NoNorm, Normalize

from improutils.acquisition import copy_to
from improutils.other import midpoint, order_points
from improutils.segmentation import to_3_channels


[docs] def plot_images( *imgs, titles=[], channels="bgr", normalize=False, ticks_off=True, title_size=32 ): """Plot multiple images in one figure. Parameters ---------- *imgs : list Arbitrary number of images to be shown. titles : list Titles for each image. channels : string Colour channels. Possible values are "bgr", "rgb" or "mono". normalize : bool If True, image will be normalized. ticks_off : bool If True, axis decorations will be hidden. title_size : int Size of the title. Returns ------- None """ assert channels.lower() in ["bgr", "rgb", "mono"], ( "Possible values for channels are: bgr, rgb or mono!" ) # f = plt.figure(figsize=(30, 20)) width_def = 60 height_def = 60 width = math.ceil(math.sqrt(len(imgs))) height = math.ceil(len(imgs) / width) height_def = height_def / 5 * width # print(height_def) if height_def > 65: height_def = 65 f = plt.figure(figsize=(width_def, height_def)) # print(str(width) + ' , ' + str(height)) for i, img in enumerate(imgs, 1): ax = f.add_subplot(height, width, i) if ticks_off: ax.axis("off") if len(titles) != 0: if len(imgs) != len(titles): print("WARNING titles length is not the same as images length!") try: ax.set_title( str(titles[i - 1]), fontdict={"fontsize": title_size, "fontweight": "medium"}, ) except IndexError: pass if channels.lower() == "mono" or img.ndim == 2: if normalize: norm = Normalize() else: norm = NoNorm() ax.imshow(img, cmap=plt.get_cmap("gray"), norm=norm) elif channels.lower() == "rgb": ax.imshow(img) else: ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
[docs] def show_images(*imgs, scale=1, window_name="Image preview"): """Display multiple images in separate resizable windows. Each image in the input list is shown in its own window. The user can click on the images to print the (x, y) coordinates of mouse clicks. The preview is terminated by pressing the 'q', 'Q', or 'Esc' key. Parameters ---------- *imgs : ndarray One or more images to display. Each image can be grayscale or color (BGR). scale : float, optional Scaling factor for the displayed image windows. Default is 1. window_name : str, optional Base name for the displayed windows. Default is "Image preview". Returns ------- None Notes ----- Known bug for Mac users: see https://gitlab.fit.cvut.cz/bi-svz/bi-svz/issues/13 """ def print_xy(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONUP: print("x = %d, y = %d" % (x, y)) for i, img in enumerate(imgs, 1): h, w = img.shape[:2] window_name_id = window_name + " " + str(i) cv2.namedWindow(window_name_id, cv2.WINDOW_NORMAL | cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow(window_name_id, int(w * scale), int(h * scale)) cv2.setMouseCallback(window_name_id, print_xy) cv2.moveWindow(window_name_id, (i - 1) * int(w * scale), 0) while 1: for i, img in enumerate(imgs, 1): cv2.imshow(window_name + " " + str(i), img) k = cv2.waitKey(0) if k == ord("q") or k == ord("Q") or k == 27: break cv2.destroyAllWindows()
[docs] def show_camera_window(*imgs, scale=1): """Open input images in separate windows. Parameters ---------- *imgs : list Arbitrary number of images to be shown. scale : double Scale of shown image window. Returns ------- None """ def print_xy(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONUP: print("x = %d, y = %d" % (x, y)) for i, img in enumerate(imgs, 1): window_name_id = "Camera capture" + " " + str(i) h, w = img.shape[:2] cv2.namedWindow(window_name_id, cv2.WINDOW_NORMAL | cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow(window_name_id, int(w * scale), int(h * scale)) cv2.setMouseCallback(window_name_id, print_xy) if len(imgs) > 1: cv2.moveWindow(window_name_id, (i - 1) * int(w * scale), 0) cv2.imshow(window_name_id, img)
[docs] def draw_rotated_rect(img, cnt): """Draw rotated rectangle with minimum area into the image, around the contour. Input image is not modified. Parameters ---------- img : ndarray Input image. cnt : ndarray Contour around which the rectangle will be drawn Returns ------- res : ndarray Image with drawn rectangle on it. rect : ndarray rectangle from cv2.minAreaRect """ res = img.copy() rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.array(box).astype(np.int32) cv2.drawContours(res, [box], -1, (255, 255, 255), 1) return res, rect
[docs] def draw_text(img, text, point, text_scale, text_color, text_thickness): """Draw rotated text into the image. Parameters ---------- img : ndarray Input image. text : string Text to be drawn. point : tuple Point where text is drawn. text_scale : double Scale of text. text_color : tuple Color of text. text_thickness : int Thickness of text. Returns ------- Output image. """ img_text = copy.deepcopy(img) # create rotated text mask cv2.putText( img_text, "{:.2f} cm".format(text), point, 0, text_scale, text_color, text_thickness, ) return img_text
[docs] def draw_real_sizes( img, rect, width_text, height_text, lbl_size_scale=2, lbl_color=(0, 0, 255), lbl_thickness=8, ): """Draw real sizes of rotated rectangle into the image. Parameters ---------- img : ndarray Input image. rect : tuple Rotated rectangle. width_text : string Width of the rectangle in the form of string. height_text : string Height of the rectangle in the form of string. lbl_size_scale : double Scale of text. lbl_color : tuple Color of text. lbl_thickness : int Thickness of text. Returns ------- Output image. """ tl, tr, br, bl = order_points(cv2.boxPoints(rect)) mid_pt_height = midpoint(tl, bl) mid_pt_width = midpoint(bl, br) # bottom-left points where labels are drawn pt_label_first = (int(mid_pt_width[0] - 10), int(mid_pt_width[1] - 10)) pt_label_second = (int(mid_pt_height[0] + 10), int(mid_pt_height[1])) result = draw_text( img, width_text, pt_label_first, lbl_size_scale, lbl_color, lbl_thickness, ) result = draw_text( result, height_text, pt_label_second, lbl_size_scale, lbl_color, lbl_thickness, ) return result
[docs] def draw_lines(img, lines): """Draw lines coming from HoughLines procedure into image. Parameters ---------- img : ndarray Input image. lines : ndarray array of lines - output of cv2.HoughLines. Returns ------- Output image. """ img_lines = to_3_channels(img) for line in lines: line = line[0] cv2.line( img_lines, (line[0], line[1]), (line[2], line[3]), (0, 0, 255), 2, cv2.LINE_AA, ) return img_lines
[docs] def color_picker(img): """Interactive tool to pick colors from an image. Displays the input image in a window and allows the user to click on points to sample their colors. Each selected color is printed in the console and annotated on the image. Press 'q', 'Q', or 'Esc' to exit. Parameters ---------- img : ndarray Input image in which colors will be sampled. Can be grayscale or color (BGR) image. Returns ------- None This function does not return a value. Selected colors are printed to the console and annotated on the displayed image. """ img = img.copy() window_name = "color picker" colors = [] def on_mouse_click(event, x, y, _, img): if event == cv2.EVENT_LBUTTONUP: colors.append(img[y, x].tolist()) cv2.putText( img, f"Point {len(colors)}: {colors[-1]}", (10, 50), cv2.FONT_HERSHEY_PLAIN, 2, (0, 0, 0), 2, ) print(f"Point {len(colors)}: {colors[-1]}") cv2.namedWindow(window_name, cv2.WINDOW_NORMAL | cv2.WINDOW_GUI_NORMAL) cv2.setMouseCallback(window_name, on_mouse_click, img) while True: cv2.imshow(window_name, img) k = cv2.waitKey(0) if k == ord("q") or k == ord("Q") or k == 27: break cv2.destroyAllWindows()