Spaces:
Runtime error
Runtime error
import numpy as np | |
import cv2 | |
from PIL import Image | |
def create_normalmap(depthmap, | |
pre_blur = None, sobel_gradient = 3, post_blur = None, | |
invert=False): | |
"""Generates normalmaps. | |
:param depthmap: depthmap that will be used to generate normalmap | |
:param pre_blur: apply gaussian blur before taking gradient, -1 for disable, otherwise kernel size | |
:param sobel_gradient: use Sobel gradient, None for regular gradient, otherwise kernel size | |
:param post_blur: apply gaussian blur after taking gradient, -1 for disable, otherwise kernel size | |
:param invert: depthmap will be inverted before calculating normalmap | |
""" | |
# https://stackoverflow.com/questions/53350391/surface-normal-calculation-from-depth-map-in-python | |
# TODO: Tiling can be improved (gradients could be matched). | |
# TODO: Implement bilateral filtering (16 bit deflickering) | |
# We invert by default, maybe there is a negative sign hiding somewhere | |
normalmap = depthmap if invert else depthmap * (-1.0) | |
normalmap = normalmap / 256.0 | |
# pre blur (only blurs z-axis) | |
if pre_blur is not None and pre_blur > 0: | |
normalmap = cv2.GaussianBlur(normalmap, (pre_blur, pre_blur), pre_blur) | |
# take gradients | |
if sobel_gradient is not None and sobel_gradient > 0: | |
zx = cv2.Sobel(np.float64(normalmap), cv2.CV_64F, 1, 0, ksize=sobel_gradient) | |
zy = cv2.Sobel(np.float64(normalmap), cv2.CV_64F, 0, 1, ksize=sobel_gradient) | |
else: | |
zy, zx = np.gradient(normalmap) | |
# combine and normalize gradients | |
normal = np.dstack((zx, -zy, np.ones_like(normalmap))) | |
# every pixel of a normal map is a normal vector, it should be a unit vector | |
n = np.linalg.norm(normal, axis=2) | |
normal[:, :, 0] /= n | |
normal[:, :, 1] /= n | |
normal[:, :, 2] /= n | |
# TODO: this probably is not a good way to do it | |
if post_blur is not None and post_blur > 0: | |
normal = cv2.GaussianBlur(normal, (post_blur, post_blur), post_blur) | |
# Normalize every vector again | |
n = np.linalg.norm(normal, axis=2) | |
normal[:, :, 0] /= n | |
normal[:, :, 1] /= n | |
normal[:, :, 2] /= n | |
# offset and rescale values to be in 0-255, so we can export them | |
normal += 1 | |
normal /= 2 | |
normal = np.clip(normal * 256, 0, 256 - 0.1) # Clipping form above is needed to avoid overflowing | |
normal = normal.astype(np.uint8) | |
return Image.fromarray(normal) | |