JeVoisBase  1.20
JeVois Smart Embedded Machine Vision Toolkit Base Modules
Share this page:
PyPostURetinex.py
Go to the documentation of this file.
1 import pyjevois
2 if pyjevois.pro: import libjevoispro as jevois
3 else: import libjevois as jevois
4 
5 import numpy as np
6 import cv2
7 
8 ## Python DNN post-processor for filtered color image
9 #
10 # Renders a filtered color image (with retinex-adjusted colors) on top of the original image, in between two
11 # user-draggable bars. Logic for the user bars here is converted from our ColorFiltering C++ module.
12 #
13 # URetinex-Net aims at recovering colors from very low light images. Hence, point your camera to a very dark area (e.g.,
14 # under your desk) to see the image enhancement provided by URetinex-Net.
15 #
16 # One of the goals of this post-processor is to demosntrate correct handling of coordinate transforms between display
17 # image, processing image, input tensor.
18 #
19 # @author Laurent Itti
20 #
21 # @email itti\@usc.edu
22 # @address University of Southern California, HNB-07A, 3641 Watt Way, Los Angeles, CA 90089-2520, USA
23 # @copyright Copyright (C) 2023 by Laurent Itti, iLab and the University of Southern California
24 # @mainurl http://jevois.org
25 # @supporturl http://jevois.org/doc
26 # @otherurl http://iLab.usc.edu
27 # @license GPL v3
28 # @distribution Unrestricted
29 # @restrictions None
30 # @ingroup pydnn
32  # ###################################################################################################
33  ## Constructor
34  def __init__(self):
35  self.rgba_map = None
36  self.dragleft = False
37  self.dragright = False
38  self.left = -1.0e20
39  self.right = -1.0e20
40  self.pp = None
41 
42  # ###################################################################################################
43  ## Get network outputs
44  def process(self, outs, preproc):
45  if len(outs) != 1: jevois.LERROR(f"Received {len(outs)} network outputs -- USING FIRST ONE")
46 
47  # Save RGBA depth map for later display:
48  self.rgba_map = cv2.cvtColor(np.squeeze(outs[0] * 255).clip(0, 255).astype('uint8').transpose(1, 2, 0),
49  cv2.COLOR_RGB2RGBA)
50 
51  # Compute overlay corner coords within the input image, for use in report():
52  self.tlx, self.tly, self.cw, self.ch = preproc.getUnscaledCropRect(0)
53 
54  # We will need to do some coordinate conversion in report(), so keep a handle to the preproc:
55  self.pp = preproc
56 
57  # ###################################################################################################
58  ## Report the latest results obtained by process() by drawing them
59  def report(self, outimg, helper, overlay, idle):
60 
61  col = 0xffffff7f # ARGB color of the vertical lines and square handles
62  siz = 20 # size of the square handles, in image pixels
63 
64  # The main thing here is to properly handle coordinates: For example
65  #
66  # - Display typically is 1920x1080 @ 0,0, or it could also be 4K
67  # - Captured video (full resolution stream for display) is typically 1920x1080 but could also be,
68  # e.g., 640x480 with scaling and translation to show up centered and as big as possible on the display
69  # - Captured video for DNN processing typically is 1024x512 @ 0,0 -> use helper.i2d() to translate/scale
70  # from image to display, or helper.d2i() from display to image
71  # - Input tensor (blob) for retinex processing typically is a rescaled and possibly letterboxed version
72  # of the processing image, e.g., to 320x180. Use preproc.b2i() or preproc.i2b()
73  # - Mouse coordinates are in screen coordinates. Here, our left and right drag handles will be in processing
74  # image coordinates, since helper.drawLine(), etc will internally call i2d() as needed.
75  #
76  # Yes, the logic below is not trivial, you need to follow it carefully. It works in a broad range of cases:
77  #
78  # - start the DNN module with dual-stream capture of 1920x1080 (for display) + 1024x512 (for processing),
79  # and the overlay and drawn handles should display correctly.
80  # - flip the 'letterbox' parameter of the pre-processor and check that graphics are still ok.
81  # - Then try the DNN module in 640x480, which uses a single capture stream, and its display is centered
82  # and scaled on the screen. Again flip the 'letterbox' parameter of the pre-processor and graphics should
83  # still look good.
84 
85  if helper is not None and overlay and self.rgba_map is not None and self.pp is not None:
86  # Processing image dims:
87  iw, ih = self.pp.imagesize()
88 
89  # Initialize the handles at 1/4 and 3/4 of image width on first video frame after module is loaded:
90  if self.left < -0.9e20:
91  self.left = 0.25 * iw
92  self.right = 0.75 * iw
93 
94  # Make sure the handles do not overlap and/or get out of the image bounds:
95  if self.left > self.right - siz:
96  if self.dragright: self.left = self.right - siz
97  else: self.right = self.left + siz
98 
99  self.left = max(siz, min(iw - siz * 2, self.left))
100  self.right = max(self.left + siz, min(iw - siz, self.right))
101 
102  # Mask and draw the overlay. To achieve this, we convert the whole result image to RGBA and then assign a
103  # zero alpha channel to all pixels to the left of the 'left' bound and to the right of the 'right' bound.
104  # First we need to convert from image coords to blob coords:
105  blob_left, blob_top = self.pp.i2b(self.left, 0.0, 0)
106  blob_right, blob_bot = self.pp.i2b(self.right, ih, 0)
107 
108  ovl = self.rgba_map
109  ovl[:, :int(blob_left), 3] = 0 # make left side transparent
110  ovl[:, int(blob_right):, 3] = 0 # make right side transparent
111 
112  # Convert box coords from input image to display ("c" is the displayed camera image):
113  tl = helper.i2d(self.tlx, self.tly, "c")
114  wh = helper.i2ds(self.cw, self.ch, "c")
115 
116  # Draw as a semi-transparent overlay. OpenGL will do scaling/stretching/blending as needed:
117  helper.drawImage("r", ovl, True, int(tl.x), int(tl.y), int(wh.x), int(wh.y), False, True)
118 
119  # Draw drag handles:
120  ovtop = self.tly # top of the overlay image (including possible preproc letterboxing)
121  ovbot = self.tly + self.ch # bottom of overlay
122  ovmid = 0.5 * (ovtop + ovbot) # vertical midpoint for our handles
123 
124  helper.drawLine(self.left, ovtop, self.left, ovbot, col)
125  helper.drawRect(self.left - siz, ovmid - siz/2, self.left, ovmid + siz/2, col, True)
126  helper.drawLine(self.right, ovtop, self.right, ovbot, col)
127  helper.drawRect(self.right, ovmid - siz/2, self.right + siz, ovmid + siz/2, col, True)
128 
129  # Adjust the left and right handles if they get clicked and dragged:
130  mp = helper.getMousePos() # in screen coordinates
131  ip = helper.d2i(mp.x, mp.y, "c") # in image coordinates
132 
133  if helper.isMouseClicked(0):
134  # Are we clicking on the left or right handle?
135  if ip.x > self.left-siz and ip.x < self.left and ip.y > (ih - siz)/2 and ip.y < (ih + siz)/2:
136  self.dragleft = True
137 
138  if ip.x > self.right and ip.x < self.right+siz and ip.y > (ih - siz)/2 and ip.y < (ih + siz)/2:
139  self.dragright = True
140 
141  if helper.isMouseDragging(0):
142  if self.dragleft: self.left = ip.x + 0.5 * siz
143  if self.dragright: self.right = ip.x - 0.5 * siz
144  # We will enforce validity of left and right on next frame, before we draw
145 
146  if helper.isMouseReleased(0):
147  self.dragleft = False
148  self.dragright = False
PyPostURetinex.PyPostURetinex.dragright
dragright
Definition: PyPostURetinex.py:37
demo.int
int
Definition: demo.py:37
PyPostURetinex.PyPostURetinex.left
left
Definition: PyPostURetinex.py:38
PyPostURetinex.PyPostURetinex.rgba_map
rgba_map
Definition: PyPostURetinex.py:35
PyPostURetinex.PyPostURetinex.process
def process(self, outs, preproc)
Get network outputs.
Definition: PyPostURetinex.py:44
PyPostURetinex.PyPostURetinex.dragleft
dragleft
Definition: PyPostURetinex.py:36
PyPostURetinex.PyPostURetinex
Python DNN post-processor for filtered color image.
Definition: PyPostURetinex.py:31
PyPostURetinex.PyPostURetinex.right
right
Definition: PyPostURetinex.py:39
PyPostURetinex.PyPostURetinex.report
def report(self, outimg, helper, overlay, idle)
Report the latest results obtained by process() by drawing them.
Definition: PyPostURetinex.py:59
PyPostURetinex.PyPostURetinex.ch
ch
Definition: PyPostURetinex.py:52
PyPostURetinex.PyPostURetinex.pp
pp
Definition: PyPostURetinex.py:40
PyPostURetinex.PyPostURetinex.__init__
def __init__(self)
Constructor.
Definition: PyPostURetinex.py:34