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