|
|
|
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): |
|
|
|
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, |
|
) |
|
|