Source code for draugr.opencv_utilities.drawing.masks.gauss_circles

import math

import cv2
import numpy

from draugr.opencv_utilities import LineTypeEnum


# ============================================================================


[docs]def ellipse_bbox(h, k, a, b, theta): """ :param h: :type h: :param k: :type k: :param a: :type a: :param b: :type b: :param theta: :type theta: :return: :rtype: """ ux = a * math.cos(theta) uy = a * math.sin(theta) vx = b * math.cos(theta + math.pi / 2) vy = b * math.sin(theta + math.pi / 2) box_halfwidth = numpy.ceil(math.sqrt(ux**2 + vx**2)) box_halfheight = numpy.ceil(math.sqrt(uy**2 + vy**2)) return ( (int(h - box_halfwidth), int(k - box_halfheight)), (int(h + box_halfwidth), int(k + box_halfheight)), )
# ---------------------------------------------------------------------------- # Rotated elliptical gradient - slow, Python-only approach
[docs]def make_gradient_v1(width, height, h, k, a, b, theta): """ :param width: :type width: :param height: :type height: :param h: :type h: :param k: :type k: :param a: :type a: :param b: :type b: :param theta: :type theta: :return: :rtype: """ # Precalculate constants st, ct = math.sin(theta), math.cos(theta) aa, bb = a**2, b**2 weights = numpy.zeros((height, width), numpy.float64) for y in range(height): for x in range(width): weights[y, x] = (((x - h) * ct + (y - k) * st) ** 2) / aa + ( ((x - h) * st - (y - k) * ct) ** 2 ) / bb return numpy.clip(1.0 - weights, 0, 1)
# ---------------------------------------------------------------------------- # Rotated elliptical gradient - faster, vectorized numpy approach
[docs]def make_gradient_v2(width, height, h, k, a, b, theta): """ :param width: :type width: :param height: :type height: :param h: :type h: :param k: :type k: :param a: :type a: :param b: :type b: :param theta: :type theta: :return: :rtype: """ # Precalculate constants st, ct = math.sin(theta), math.cos(theta) aa, bb = a**2, b**2 # Generate (x,y) coordinate arrays y, x = numpy.mgrid[-k : height - k, -h : width - h] # Calculate the weight for each pixel weights = (((x * ct + y * st) ** 2) / aa) + (((x * st - y * ct) ** 2) / bb) return numpy.clip(1.0 - weights, 0, 1)
# ============================================================================ if __name__ == "__main__": from pathlib import Path from apppath import ensure_existence from draugr.opencv_utilities import show_image basep = ensure_existence(Path("exclude")) def draw_image(a, b, theta, inner_scale, save_intermediate=False): """ :param a: :type a: :param b: :type b: :param theta: :type theta: :param inner_scale: :type inner_scale: :param save_intermediate: :type save_intermediate: :return: :rtype: """ # Calculate the image size needed to draw this and center the ellipse _, (h, k) = ellipse_bbox(0, 0, a, b, theta) # Ellipse center h += 2 # Add small margin k += 2 # Add small margin width, height = (h * 2 + 1, k * 2 + 1) # Canvas size # Parameters defining the two ellipses for OpenCV (a RotatedRect structure) ellipse_outer = ((h, k), (a * 2, b * 2), math.degrees(theta)) ellipse_inner = ( (h, k), (a * 2 * inner_scale, b * 2 * inner_scale), math.degrees(theta), ) # Generate the transparency layer -- the outer ellipse filled and anti-aliased transparency = numpy.zeros((height, width), numpy.uint8) cv2.ellipse( transparency, ellipse_outer, 255, -1, LineTypeEnum.anti_aliased.value ) if save_intermediate: show_image( transparency, wait=True # save_path = basep/"eligrad-t.png" ) # Generate the gradient and scale it to 8bit grayscale range intensity = numpy.uint8( make_gradient_v1(width, height, h, k, a, b, theta) * 255 ) if save_intermediate: show_image( intensity, wait=True # save_path = str(basep / "eligrad-i1.png") ) # Draw the inter ellipse filled and anti-aliased cv2.ellipse(intensity, ellipse_inner, 255, -1, LineTypeEnum.anti_aliased.value) if save_intermediate: show_image( intensity, # save_path = str(basep / "eligrad-i2.png") wait=True, ) # Turn it into a BGRA image result = cv2.merge([intensity, intensity, intensity, transparency]) return result # ============================================================================ a, b = (360.0, 200.0) # Semi-major and semi-minor axis theta = math.radians(40.0) # Ellipse rotation (radians) inner_scale = 0.6 # Scale of the inner full-white ellipse show_image( draw_image(a, b, theta, inner_scale, True), wait=True # save_path = str(basep/"eligrad.png") ) # ============================================================================ rows = [] for j in range(0, 4, 1): cols = [] for i in range(0, 90, 10): tile = numpy.zeros((170, 170, 4), numpy.uint8) image = draw_image(80.0, 50.0, math.radians(i + j * 90), 0.6) tile[: image.shape[0], : image.shape[1]] = image cols.append(tile) rows.append(numpy.hstack(cols)) show_image( numpy.vstack(rows), # save_path = str(basep/"eligrad-m.png") wait=True, )