Spaces:
Paused
Paused
# Copyright (c) Facebook, Inc. and its affiliates. | |
import logging | |
import numpy as np | |
from typing import List, Optional, Tuple | |
import cv2 | |
import torch | |
from densepose.structures import DensePoseDataRelative | |
from ..structures import DensePoseChartResult | |
from .base import Boxes, Image, MatrixVisualizer | |
class DensePoseResultsVisualizer: | |
def visualize( | |
self, | |
image_bgr: Image, | |
results_and_boxes_xywh: Tuple[Optional[List[DensePoseChartResult]], Optional[Boxes]], | |
) -> Image: | |
densepose_result, boxes_xywh = results_and_boxes_xywh | |
if densepose_result is None or boxes_xywh is None: | |
return image_bgr | |
boxes_xywh = boxes_xywh.cpu().numpy() | |
context = self.create_visualization_context(image_bgr) | |
for i, result in enumerate(densepose_result): | |
iuv_array = torch.cat( | |
(result.labels[None].type(torch.float32), result.uv * 255.0) | |
).type(torch.uint8) | |
self.visualize_iuv_arr(context, iuv_array.cpu().numpy(), boxes_xywh[i]) | |
image_bgr = self.context_to_image_bgr(context) | |
return image_bgr | |
def create_visualization_context(self, image_bgr: Image): | |
return image_bgr | |
def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh) -> None: | |
pass | |
def context_to_image_bgr(self, context): | |
return context | |
def get_image_bgr_from_context(self, context): | |
return context | |
class DensePoseMaskedColormapResultsVisualizer(DensePoseResultsVisualizer): | |
def __init__( | |
self, | |
data_extractor, | |
segm_extractor, | |
inplace=True, | |
cmap=cv2.COLORMAP_PARULA, | |
alpha=0.7, | |
val_scale=1.0, | |
**kwargs, | |
): | |
self.mask_visualizer = MatrixVisualizer( | |
inplace=inplace, cmap=cmap, val_scale=val_scale, alpha=alpha | |
) | |
self.data_extractor = data_extractor | |
self.segm_extractor = segm_extractor | |
def context_to_image_bgr(self, context): | |
return context | |
def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh) -> None: | |
image_bgr = self.get_image_bgr_from_context(context) | |
matrix = self.data_extractor(iuv_arr) | |
segm = self.segm_extractor(iuv_arr) | |
mask = np.zeros(matrix.shape, dtype=np.uint8) | |
mask[segm > 0] = 1 | |
image_bgr = self.mask_visualizer.visualize(image_bgr, mask, matrix, bbox_xywh) | |
def _extract_i_from_iuvarr(iuv_arr): | |
return iuv_arr[0, :, :] | |
def _extract_u_from_iuvarr(iuv_arr): | |
return iuv_arr[1, :, :] | |
def _extract_v_from_iuvarr(iuv_arr): | |
return iuv_arr[2, :, :] | |
class DensePoseResultsMplContourVisualizer(DensePoseResultsVisualizer): | |
def __init__(self, levels=10, **kwargs): | |
self.levels = levels | |
self.plot_args = kwargs | |
def create_visualization_context(self, image_bgr: Image): | |
import matplotlib.pyplot as plt | |
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas | |
context = {} | |
context["image_bgr"] = image_bgr | |
dpi = 100 | |
height_inches = float(image_bgr.shape[0]) / dpi | |
width_inches = float(image_bgr.shape[1]) / dpi | |
fig = plt.figure(figsize=(width_inches, height_inches), dpi=dpi) | |
plt.axes([0, 0, 1, 1]) | |
plt.axis("off") | |
context["fig"] = fig | |
canvas = FigureCanvas(fig) | |
context["canvas"] = canvas | |
extent = (0, image_bgr.shape[1], image_bgr.shape[0], 0) | |
plt.imshow(image_bgr[:, :, ::-1], extent=extent) | |
return context | |
def context_to_image_bgr(self, context): | |
fig = context["fig"] | |
w, h = map(int, fig.get_size_inches() * fig.get_dpi()) | |
canvas = context["canvas"] | |
canvas.draw() | |
image_1d = np.fromstring(canvas.tostring_rgb(), dtype="uint8") | |
image_rgb = image_1d.reshape(h, w, 3) | |
image_bgr = image_rgb[:, :, ::-1].copy() | |
return image_bgr | |
def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh: Boxes) -> None: | |
import matplotlib.pyplot as plt | |
u = _extract_u_from_iuvarr(iuv_arr).astype(float) / 255.0 | |
v = _extract_v_from_iuvarr(iuv_arr).astype(float) / 255.0 | |
extent = ( | |
bbox_xywh[0], | |
bbox_xywh[0] + bbox_xywh[2], | |
bbox_xywh[1], | |
bbox_xywh[1] + bbox_xywh[3], | |
) | |
plt.contour(u, self.levels, extent=extent, **self.plot_args) | |
plt.contour(v, self.levels, extent=extent, **self.plot_args) | |
class DensePoseResultsCustomContourVisualizer(DensePoseResultsVisualizer): | |
""" | |
Contour visualization using marching squares | |
""" | |
def __init__(self, levels=10, **kwargs): | |
# TODO: colormap is hardcoded | |
cmap = cv2.COLORMAP_PARULA | |
if isinstance(levels, int): | |
self.levels = np.linspace(0, 1, levels) | |
else: | |
self.levels = levels | |
if "linewidths" in kwargs: | |
self.linewidths = kwargs["linewidths"] | |
else: | |
self.linewidths = [1] * len(self.levels) | |
self.plot_args = kwargs | |
img_colors_bgr = cv2.applyColorMap((self.levels * 255).astype(np.uint8), cmap) | |
self.level_colors_bgr = [ | |
[int(v) for v in img_color_bgr.ravel()] for img_color_bgr in img_colors_bgr | |
] | |
def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh: Boxes) -> None: | |
image_bgr = self.get_image_bgr_from_context(context) | |
segm = _extract_i_from_iuvarr(iuv_arr) | |
u = _extract_u_from_iuvarr(iuv_arr).astype(float) / 255.0 | |
v = _extract_v_from_iuvarr(iuv_arr).astype(float) / 255.0 | |
self._contours(image_bgr, u, segm, bbox_xywh) | |
self._contours(image_bgr, v, segm, bbox_xywh) | |
def _contours(self, image_bgr, arr, segm, bbox_xywh): | |
for part_idx in range(1, DensePoseDataRelative.N_PART_LABELS + 1): | |
mask = segm == part_idx | |
if not np.any(mask): | |
continue | |
arr_min = np.amin(arr[mask]) | |
arr_max = np.amax(arr[mask]) | |
I, J = np.nonzero(mask) | |
i0 = np.amin(I) | |
i1 = np.amax(I) + 1 | |
j0 = np.amin(J) | |
j1 = np.amax(J) + 1 | |
if (j1 == j0 + 1) or (i1 == i0 + 1): | |
continue | |
Nw = arr.shape[1] - 1 | |
Nh = arr.shape[0] - 1 | |
for level_idx, level in enumerate(self.levels): | |
if (level < arr_min) or (level > arr_max): | |
continue | |
vp = arr[i0:i1, j0:j1] >= level | |
bin_codes = vp[:-1, :-1] + vp[1:, :-1] * 2 + vp[1:, 1:] * 4 + vp[:-1, 1:] * 8 | |
mp = mask[i0:i1, j0:j1] | |
bin_mask_codes = mp[:-1, :-1] + mp[1:, :-1] * 2 + mp[1:, 1:] * 4 + mp[:-1, 1:] * 8 | |
it = np.nditer(bin_codes, flags=["multi_index"]) | |
color_bgr = self.level_colors_bgr[level_idx] | |
linewidth = self.linewidths[level_idx] | |
while not it.finished: | |
if (it[0] != 0) and (it[0] != 15): | |
i, j = it.multi_index | |
if bin_mask_codes[i, j] != 0: | |
self._draw_line( | |
image_bgr, | |
arr, | |
mask, | |
level, | |
color_bgr, | |
linewidth, | |
it[0], | |
it.multi_index, | |
bbox_xywh, | |
Nw, | |
Nh, | |
(i0, j0), | |
) | |
it.iternext() | |
def _draw_line( | |
self, | |
image_bgr, | |
arr, | |
mask, | |
v, | |
color_bgr, | |
linewidth, | |
bin_code, | |
multi_idx, | |
bbox_xywh, | |
Nw, | |
Nh, | |
offset, | |
): | |
lines = self._bin_code_2_lines(arr, v, bin_code, multi_idx, Nw, Nh, offset) | |
x0, y0, w, h = bbox_xywh | |
x1 = x0 + w | |
y1 = y0 + h | |
for line in lines: | |
x0r, y0r = line[0] | |
x1r, y1r = line[1] | |
pt0 = (int(x0 + x0r * (x1 - x0)), int(y0 + y0r * (y1 - y0))) | |
pt1 = (int(x0 + x1r * (x1 - x0)), int(y0 + y1r * (y1 - y0))) | |
cv2.line(image_bgr, pt0, pt1, color_bgr, linewidth) | |
def _bin_code_2_lines(self, arr, v, bin_code, multi_idx, Nw, Nh, offset): | |
i0, j0 = offset | |
i, j = multi_idx | |
i += i0 | |
j += j0 | |
v0, v1, v2, v3 = arr[i, j], arr[i + 1, j], arr[i + 1, j + 1], arr[i, j + 1] | |
x0i = float(j) / Nw | |
y0j = float(i) / Nh | |
He = 1.0 / Nh | |
We = 1.0 / Nw | |
if (bin_code == 1) or (bin_code == 14): | |
a = (v - v0) / (v1 - v0) | |
b = (v - v0) / (v3 - v0) | |
pt1 = (x0i, y0j + a * He) | |
pt2 = (x0i + b * We, y0j) | |
return [(pt1, pt2)] | |
elif (bin_code == 2) or (bin_code == 13): | |
a = (v - v0) / (v1 - v0) | |
b = (v - v1) / (v2 - v1) | |
pt1 = (x0i, y0j + a * He) | |
pt2 = (x0i + b * We, y0j + He) | |
return [(pt1, pt2)] | |
elif (bin_code == 3) or (bin_code == 12): | |
a = (v - v0) / (v3 - v0) | |
b = (v - v1) / (v2 - v1) | |
pt1 = (x0i + a * We, y0j) | |
pt2 = (x0i + b * We, y0j + He) | |
return [(pt1, pt2)] | |
elif (bin_code == 4) or (bin_code == 11): | |
a = (v - v1) / (v2 - v1) | |
b = (v - v3) / (v2 - v3) | |
pt1 = (x0i + a * We, y0j + He) | |
pt2 = (x0i + We, y0j + b * He) | |
return [(pt1, pt2)] | |
elif (bin_code == 6) or (bin_code == 9): | |
a = (v - v0) / (v1 - v0) | |
b = (v - v3) / (v2 - v3) | |
pt1 = (x0i, y0j + a * He) | |
pt2 = (x0i + We, y0j + b * He) | |
return [(pt1, pt2)] | |
elif (bin_code == 7) or (bin_code == 8): | |
a = (v - v0) / (v3 - v0) | |
b = (v - v3) / (v2 - v3) | |
pt1 = (x0i + a * We, y0j) | |
pt2 = (x0i + We, y0j + b * He) | |
return [(pt1, pt2)] | |
elif bin_code == 5: | |
a1 = (v - v0) / (v1 - v0) | |
b1 = (v - v1) / (v2 - v1) | |
pt11 = (x0i, y0j + a1 * He) | |
pt12 = (x0i + b1 * We, y0j + He) | |
a2 = (v - v0) / (v3 - v0) | |
b2 = (v - v3) / (v2 - v3) | |
pt21 = (x0i + a2 * We, y0j) | |
pt22 = (x0i + We, y0j + b2 * He) | |
return [(pt11, pt12), (pt21, pt22)] | |
elif bin_code == 10: | |
a1 = (v - v0) / (v3 - v0) | |
b1 = (v - v0) / (v1 - v0) | |
pt11 = (x0i + a1 * We, y0j) | |
pt12 = (x0i, y0j + b1 * He) | |
a2 = (v - v1) / (v2 - v1) | |
b2 = (v - v3) / (v2 - v3) | |
pt21 = (x0i + a2 * We, y0j + He) | |
pt22 = (x0i + We, y0j + b2 * He) | |
return [(pt11, pt12), (pt21, pt22)] | |
return [] | |
try: | |
import matplotlib | |
matplotlib.use("Agg") | |
DensePoseResultsContourVisualizer = DensePoseResultsMplContourVisualizer | |
except ModuleNotFoundError: | |
logger = logging.getLogger(__name__) | |
logger.warning("Could not import matplotlib, using custom contour visualizer") | |
DensePoseResultsContourVisualizer = DensePoseResultsCustomContourVisualizer | |
class DensePoseResultsFineSegmentationVisualizer(DensePoseMaskedColormapResultsVisualizer): | |
def __init__(self, inplace=False, cmap=cv2.COLORMAP_PARULA, alpha=1, **kwargs): | |
super(DensePoseResultsFineSegmentationVisualizer, self).__init__( | |
_extract_i_from_iuvarr, | |
_extract_i_from_iuvarr, | |
inplace, | |
cmap, | |
alpha, | |
val_scale=255.0 / DensePoseDataRelative.N_PART_LABELS, | |
**kwargs, | |
) | |
class DensePoseResultsUVisualizer(DensePoseMaskedColormapResultsVisualizer): | |
def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): | |
super(DensePoseResultsUVisualizer, self).__init__( | |
_extract_u_from_iuvarr, | |
_extract_i_from_iuvarr, | |
inplace, | |
cmap, | |
alpha, | |
val_scale=1.0, | |
**kwargs, | |
) | |
class DensePoseResultsVVisualizer(DensePoseMaskedColormapResultsVisualizer): | |
def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): | |
super(DensePoseResultsVVisualizer, self).__init__( | |
_extract_v_from_iuvarr, | |
_extract_i_from_iuvarr, | |
inplace, | |
cmap, | |
alpha, | |
val_scale=1.0, | |
**kwargs, | |
) | |