Skip to content

Commit 5e234c3

Browse files
EzwardDocGarbanzo
andauthored
1109 refactor augmentations/transformations (#1111)
* moved transformations to cv.py; added colorspace transforms * add parts for crop and trapeze transformations * Update training to use new transformations - image_transformations.py use cv.py parts for transforms - augmentations2.py remove old transformations - training.py updated to allow for transformations both before and after training augmentation. - cfg_complete.py added new tranformation config and a lot of documentation comments. * Port albumentations augmentations from Dirk's branch - replace imgaug with albumentations - update install to include albumentations * transforms from gray to color, cvcam resizes - conversions from grey to BGR and RGB - fixed CvCam so it will resize the image if the camera does not produce the desired size. * Update deep learning to show inference image - Now we can show the image used for inference when in autopilot mode. set `SHOW_PILOT_IMAGE` to True. * augmentations use albumentations library - ported from Dirk's branch - remove transformations from ImageAugmentation class in favor of new transformations in ImageTransformations class * Can now add a custom image transformation to pipeline - name the label starting with "CUSTOM", like "CUSTOM_CROP" - include config to use to get the module and class names. these being with the custom label and end with "_MODULE" and "_CLASS" respectively, like "CUSTOM_CROP_MODULE" and "CUSTOM_CROP_CLASS" - The custom transformer class must take a Config instance in the constructor and must have a run method that takes and image and returns an image. * Change custom transformation loader to use file path - the prior design did not work for both driving from the mycar folder and training (which uses the donkey module) - the new design dynamically loads the module given it's file path and caches it. As long as the file path is either an absolute path then this will work when driving or training without changing myconfig.py. - this design allows more then on custom transformer class per file. * Change imgaug -> albumentations in the UI. Update yaml files to remove opencv-headless which clashes w/ opencv. Unfreeze numpy version. * Peg numpy to 1.19 in order to be aligned w/ tf 2.2.0 and replace remaining instances of MULTIPLY with BRIGHTNESS. * Align interface of ImageTransformations with ImageAugmentations to take a config and a string, representing the key of transformations in the config file, and another optional string of post transformations. Update ui to support pre and post transformations. * version='4.4.dev7' --------- Co-authored-by: DocGarbanzo <47540921+DocGarbanzo@users.noreply.github.com>
1 parent 41b326d commit 5e234c3

File tree

16 files changed

+1153
-203
lines changed

16 files changed

+1153
-203
lines changed

donkeycar/management/kivy_ui.py

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from kivy.properties import NumericProperty, ObjectProperty, StringProperty, \
2626
ListProperty, BooleanProperty
2727
from kivy.uix.label import Label
28+
from kivy.uix.button import Button
2829
from kivy.uix.popup import Popup
2930
from kivy.lang.builder import Builder
3031
from kivy.core.window import Window
@@ -33,6 +34,7 @@
3334
from kivy.uix.spinner import SpinnerOption, Spinner
3435

3536
from donkeycar import load_config
37+
from donkeycar.parts.image_transformations import ImageTransformations
3638
from donkeycar.parts.tub_v2 import Tub
3739
from donkeycar.pipeline.augmentations import ImageAugmentation
3840
from donkeycar.pipeline.database import PilotDatabase
@@ -739,6 +741,8 @@ def augment(self, img_arr):
739741
img_arr = pilot_screen().transformation.run(img_arr)
740742
if pilot_screen().aug_list:
741743
img_arr = pilot_screen().augmentation.run(img_arr)
744+
if pilot_screen().post_trans_list:
745+
img_arr = pilot_screen().post_transformation.run(img_arr)
742746
return img_arr
743747

744748
def get_image(self, record):
@@ -778,6 +782,59 @@ def get_image(self, record):
778782
return img_arr
779783

780784

785+
class TransformationPopup(Popup):
786+
""" Transformation popup window"""
787+
title = StringProperty()
788+
transformations = \
789+
["TRAPEZE", "CROP", "RGB2BGR", "BGR2RGB", "RGB2HSV", "HSV2RGB",
790+
"BGR2HSV", "HSV2BGR", "RGB2GRAY", "RBGR2GRAY", "HSV2GRAY", "GRAY2RGB",
791+
"GRAY2BGR", "CANNY", "BLUR", "RESIZE", "SCALE"]
792+
transformations_obj = ObjectProperty()
793+
selected = ListProperty()
794+
795+
def __init__(self, selected, **kwargs):
796+
super().__init__(**kwargs)
797+
for t in self.transformations:
798+
btn = Button(text=t)
799+
btn.bind(on_release=self.toggle_transformation)
800+
self.ids.trafo_list.add_widget(btn)
801+
self.selected = selected
802+
803+
def toggle_transformation(self, btn):
804+
trafo = btn.text
805+
if trafo in self.selected:
806+
self.selected.remove(trafo)
807+
else:
808+
self.selected.append(trafo)
809+
810+
def on_selected(self, obj, select):
811+
self.ids.selected_list.clear_widgets()
812+
for l in self.selected:
813+
lab = Label(text=l)
814+
self.ids.selected_list.add_widget(lab)
815+
self.transformations_obj.selected = self.selected
816+
817+
818+
class Transformations(Button):
819+
""" Base class for transformation widgets"""
820+
title = StringProperty(None)
821+
pilot_screen = ObjectProperty()
822+
is_post = False
823+
selected = ListProperty()
824+
825+
def open_popup(self):
826+
popup = TransformationPopup(title=self.title, transformations_obj=self,
827+
selected=self.selected)
828+
popup.open()
829+
830+
def on_selected(self, obj, select):
831+
Logger.info(f"Selected {select}")
832+
if self.is_post:
833+
self.pilot_screen.post_trans_list = self.selected
834+
else:
835+
self.pilot_screen.trans_list = self.selected
836+
837+
781838
class PilotScreen(Screen):
782839
""" Screen to do the pilot vs pilot comparison ."""
783840
index = NumericProperty(None, force_dispatch=True)
@@ -787,6 +844,8 @@ class PilotScreen(Screen):
787844
augmentation = ObjectProperty()
788845
trans_list = ListProperty(force_dispatch=True)
789846
transformation = ObjectProperty()
847+
post_trans_list = ListProperty(force_dispatch=True)
848+
post_transformation = ObjectProperty()
790849
config = ObjectProperty()
791850

792851
def on_index(self, obj, index):
@@ -829,13 +888,16 @@ def set_brightness(self, val=None):
829888
if not self.config:
830889
return
831890
if self.ids.button_bright.state == 'down':
832-
self.config.AUG_MULTIPLY_RANGE = (val, val)
833-
if 'MULTIPLY' not in self.aug_list:
834-
self.aug_list.append('MULTIPLY')
835-
elif 'MULTIPLY' in self.aug_list:
836-
self.aug_list.remove('MULTIPLY')
837-
# update dependency
838-
self.on_aug_list(None, None)
891+
self.config.AUG_BRIGHTNESS_RANGE = (val, val)
892+
if 'BRIGHTNESS' not in self.aug_list:
893+
self.aug_list.append('BRIGHTNESS')
894+
else:
895+
# Since we only changed the content of the config here,
896+
# self.on_aug_list() would not be called, but we want to update
897+
# the augmentation. Hence, update the dependency manually here.
898+
self.on_aug_list(None, None)
899+
elif 'BRIGHTNESS' in self.aug_list:
900+
self.aug_list.remove('BRIGHTNESS')
839901

840902
def set_blur(self, val=None):
841903
if not self.config:
@@ -853,14 +915,24 @@ def on_aug_list(self, obj, aug_list):
853915
if not self.config:
854916
return
855917
self.config.AUGMENTATIONS = self.aug_list
856-
self.augmentation = ImageAugmentation(self.config, 'AUGMENTATIONS')
918+
self.augmentation = ImageAugmentation(
919+
self.config, 'AUGMENTATIONS', always_apply=True)
857920
self.on_current_record(None, self.current_record)
858921

859922
def on_trans_list(self, obj, trans_list):
860923
if not self.config:
861924
return
862925
self.config.TRANSFORMATIONS = self.trans_list
863-
self.transformation = ImageAugmentation(self.config, 'TRANSFORMATIONS')
926+
self.transformation = ImageTransformations(
927+
self.config, 'TRANSFORMATIONS')
928+
self.on_current_record(None, self.current_record)
929+
930+
def on_post_trans_list(self, obj, trans_list):
931+
if not self.config:
932+
return
933+
self.config.POST_TRANSFORMATIONS = self.post_trans_list
934+
self.post_transformation = ImageTransformations(
935+
self.config, 'POST_TRANSFORMATIONS')
864936
self.on_current_record(None, self.current_record)
865937

866938
def set_mask(self, state):

donkeycar/management/makemovie.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def run(self, args, parser):
8484
def draw_line_into_image(angle, throttle, is_left, img, color):
8585
import cv2
8686

87-
height, width, _ = img.shape
87+
height = img.shape[0]
88+
width = img.shape[1]
8889
length = height
8990
a1 = angle * 45.0
9091
l1 = throttle * length

donkeycar/management/ui.kv

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,30 @@
381381
text: root.file_path
382382

383383

384+
<TransformationPopup>:
385+
size_hint: 1.0, 1.0
386+
auto_dismiss: False
387+
pos_hint: {'center_x': .5, 'center_y': .5}
388+
389+
BoxLayout:
390+
orientation: "horizontal"
391+
BoxLayout:
392+
orientation: 'vertical'
393+
id: selected_list
394+
spacing: 20
395+
396+
BoxLayout:
397+
orientation: 'vertical'
398+
spacing: 20
399+
BoxLayout:
400+
orientation: 'vertical'
401+
id: trafo_list
402+
Button:
403+
text: "Close"
404+
size_hint_y: 0.1
405+
on_release: root.dismiss()
406+
407+
384408
<PilotScreen>:
385409
config: app.tub_screen.ids.config_manager.config
386410
BoxLayout:
@@ -443,9 +467,9 @@
443467
on_press: root.set_brightness(slider_bright.value)
444468
Slider:
445469
id: slider_bright
446-
value: 1
447-
min: 0
448-
max: 3
470+
value: 0
471+
min: -0.5
472+
max: 0.5
449473
on_value: root.set_brightness(self.value)
450474
ToggleButton:
451475
id: button_blur
@@ -455,18 +479,24 @@
455479
Slider:
456480
id: slider_blur
457481
value: 0
458-
min: 0
459-
max: 4
482+
min: 0.001
483+
max: 3
460484
on_value: root.set_blur(self.value)
461485
PaddedBoxLayout:
462-
ToggleButton:
463-
id: button_mask
464-
text: 'Trapezoidal mask'
465-
on_press: root.set_mask(self.state)
466-
ToggleButton:
467-
id: button_crop
468-
text: 'Cropping mask'
469-
on_press: root.set_crop(self.state)
486+
Transformations:
487+
id: pre_transformation
488+
title: 'Pre-Augmentation Transformations'
489+
text: 'Set pre transformation'
490+
pilot_screen: root
491+
is_post: False
492+
on_press: self.open_popup()
493+
Transformations:
494+
id: post_transformation
495+
title: 'Post-Augmentation Transformations'
496+
text: 'Set post transformation'
497+
pilot_screen: root
498+
is_post: True
499+
on_press: self.open_popup()
470500
PaddedBoxLayout:
471501
size_hint_y: 0.5
472502
ControlPanel:

0 commit comments

Comments
 (0)