diff --git a/labelImg.py b/labelImg.py
index 2cac92a5..b11f9c15 100755
--- a/labelImg.py
+++ b/labelImg.py
@@ -34,6 +34,7 @@ from libs.shape import Shape, DEFAULT_LINE_COLOR, DEFAULT_FILL_COLOR
from libs.stringBundle import StringBundle
from libs.canvas import Canvas
from libs.zoomWidget import ZoomWidget
+from libs.lightWidget import LightWidget
from libs.labelDialog import LabelDialog
from libs.colorDialog import ColorDialog
from libs.labelFile import LabelFile, LabelFileError, LabelFileFormat
@@ -180,10 +181,12 @@ class MainWindow(QMainWindow, WindowMixin):
self.file_dock.setWidget(file_list_container)
self.zoom_widget = ZoomWidget()
+ self.light_widget = LightWidget(get_str('lightWidgetTitle'))
self.color_dialog = ColorDialog(parent=self)
self.canvas = Canvas(parent=self)
self.canvas.zoomRequest.connect(self.zoom_request)
+ self.canvas.lightRequest.connect(self.light_request)
self.canvas.set_drawing_shape_to_square(settings.get(SETTING_DRAW_SQUARE, False))
scroll = QScrollArea()
@@ -326,6 +329,26 @@ class MainWindow(QMainWindow, WindowMixin):
self.MANUAL_ZOOM: lambda: 1,
}
+ light = QWidgetAction(self)
+ light.setDefaultWidget(self.light_widget)
+ self.light_widget.setWhatsThis(
+ u"Brighten or darken current image. Also accessible with"
+ " %s and %s from the canvas." % (format_shortcut("Ctrl+Shift+[-+]"),
+ format_shortcut("Ctrl+Shift+Wheel")))
+ self.light_widget.setEnabled(False)
+
+ light_brighten = action(get_str('lightbrighten'), partial(self.add_light, 10),
+ 'Ctrl+Shift++', 'light_lighten', get_str('lightbrightenDetail'), enabled=False)
+ light_darken = action(get_str('lightdarken'), partial(self.add_light, -10),
+ 'Ctrl+Shift+-', 'light_darken', get_str('lightdarkenDetail'), enabled=False)
+ light_org = action(get_str('lightreset'), partial(self.set_light, 50),
+ 'Ctrl+Shift+=', 'light_reset', get_str('lightresetDetail'), checkable=True, enabled=False)
+ light_org.setChecked(True)
+
+ # Group light controls into a list for easier toggling.
+ light_actions = (self.light_widget, light_brighten,
+ light_darken, light_org)
+
edit = action(get_str('editLabel'), self.edit_label,
'Ctrl+E', 'edit', get_str('editLabelDetail'),
enabled=False)
@@ -364,6 +387,8 @@ class MainWindow(QMainWindow, WindowMixin):
zoom=zoom, zoomIn=zoom_in, zoomOut=zoom_out, zoomOrg=zoom_org,
fitWindow=fit_window, fitWidth=fit_width,
zoomActions=zoom_actions,
+ lightBrighten=light_brighten, lightDarken=light_darken, lightOrg=light_org,
+ lightActions=light_actions,
fileMenuActions=(
open, open_dir, save, save_as, close, reset_all, quit),
beginner=(), advanced=(),
@@ -411,7 +436,8 @@ class MainWindow(QMainWindow, WindowMixin):
labels, advanced_mode, None,
hide_all, show_all, None,
zoom_in, zoom_out, zoom_org, None,
- fit_window, fit_width))
+ fit_window, fit_width, None,
+ light_brighten, light_darken, light_org))
self.menus.file.aboutToShow.connect(self.update_file_menu)
@@ -424,7 +450,8 @@ class MainWindow(QMainWindow, WindowMixin):
self.tools = self.toolbar('Tools')
self.actions.beginner = (
open, open_dir, change_save_dir, open_next_image, open_prev_image, verify, save, save_format, None, create, copy, delete, None,
- zoom_in, zoom, zoom_out, fit_window, fit_width)
+ zoom_in, zoom, zoom_out, fit_window, fit_width, None,
+ light_brighten, light, light_darken, light_org)
self.actions.advanced = (
open, open_dir, change_save_dir, open_next_image, open_prev_image, save, save_format, None,
@@ -500,6 +527,7 @@ class MainWindow(QMainWindow, WindowMixin):
# Callbacks:
self.zoom_widget.valueChanged.connect(self.paint_canvas)
+ self.light_widget.valueChanged.connect(self.paint_canvas)
self.populate_mode_actions()
@@ -601,6 +629,8 @@ class MainWindow(QMainWindow, WindowMixin):
"""Enable/Disable widgets which depend on an opened image."""
for z in self.actions.zoomActions:
z.setEnabled(value)
+ for z in self.actions.lightActions:
+ z.setEnabled(value)
for action in self.actions.onLoadActive:
action.setEnabled(value)
@@ -1032,6 +1062,9 @@ class MainWindow(QMainWindow, WindowMixin):
h_bar.setValue(new_h_bar_value)
v_bar.setValue(new_v_bar_value)
+ def light_request(self, delta):
+ self.add_light(5*delta // (8 * 15))
+
def set_fit_window(self, value=True):
if value:
self.actions.fitWidth.setChecked(False)
@@ -1044,6 +1077,15 @@ class MainWindow(QMainWindow, WindowMixin):
self.zoom_mode = self.FIT_WIDTH if value else self.MANUAL_ZOOM
self.adjust_scale()
+ def set_light(self, value):
+ self.actions.lightOrg.setChecked(int(value) == 50)
+ # Arithmetic on scaling factor often results in float
+ # Convert to int to avoid type errors
+ self.light_widget.setValue(int(value))
+
+ def add_light(self, increment=10):
+ self.set_light(self.light_widget.value() + increment)
+
def toggle_polygons(self, value):
for item, shape in self.items_to_shapes.items():
item.setCheckState(Qt.Checked if value else Qt.Unchecked)
@@ -1172,6 +1214,7 @@ class MainWindow(QMainWindow, WindowMixin):
def paint_canvas(self):
assert not self.image.isNull(), "cannot paint null image"
self.canvas.scale = 0.01 * self.zoom_widget.value()
+ self.canvas.overlay_color = self.light_widget.color()
self.canvas.label_font_size = int(0.02 * max(self.image.width(), self.image.height()))
self.canvas.adjustSize()
self.canvas.update()
diff --git a/libs/canvas.py b/libs/canvas.py
index 98deba62..4a8d9586 100644
--- a/libs/canvas.py
+++ b/libs/canvas.py
@@ -23,6 +23,7 @@ CURSOR_GRAB = Qt.OpenHandCursor
class Canvas(QWidget):
zoomRequest = pyqtSignal(int)
+ lightRequest = pyqtSignal(int)
scrollRequest = pyqtSignal(int, int)
newShape = pyqtSignal()
selectionChanged = pyqtSignal(bool)
@@ -47,6 +48,7 @@ class Canvas(QWidget):
self.prev_point = QPointF()
self.offsets = QPointF(), QPointF()
self.scale = 1.0
+ self.overlay_color = None
self.label_font_size = 8
self.pixmap = QPixmap()
self.visible = {}
@@ -503,7 +505,15 @@ class Canvas(QWidget):
p.scale(self.scale, self.scale)
p.translate(self.offset_to_center())
- p.drawPixmap(0, 0, self.pixmap)
+ temp = self.pixmap
+ if self.overlay_color:
+ temp = QPixmap(self.pixmap)
+ painter = QPainter(temp)
+ painter.setCompositionMode(painter.CompositionMode_Overlay)
+ painter.fillRect(temp.rect(), self.overlay_color)
+ painter.end()
+
+ p.drawPixmap(0, 0, temp)
Shape.scale = self.scale
Shape.label_font_size = self.label_font_size
for shape in self.shapes:
@@ -607,7 +617,9 @@ class Canvas(QWidget):
v_delta = delta.y()
mods = ev.modifiers()
- if Qt.ControlModifier == int(mods) and v_delta:
+ if int(Qt.ControlModifier) | int(Qt.ShiftModifier) == int(mods) and v_delta:
+ self.lightRequest.emit(v_delta)
+ elif Qt.ControlModifier == int(mods) and v_delta:
self.zoomRequest.emit(v_delta)
else:
v_delta and self.scrollRequest.emit(v_delta, Qt.Vertical)
diff --git a/libs/lightWidget.py b/libs/lightWidget.py
new file mode 100644
index 00000000..f177ed7d
--- /dev/null
+++ b/libs/lightWidget.py
@@ -0,0 +1,33 @@
+try:
+ from PyQt5.QtGui import *
+ from PyQt5.QtCore import *
+ from PyQt5.QtWidgets import *
+except ImportError:
+ from PyQt4.QtGui import *
+ from PyQt4.QtCore import *
+
+
+class LightWidget(QSpinBox):
+
+ def __init__(self, title, value=50):
+ super(LightWidget, self).__init__()
+ self.setButtonSymbols(QAbstractSpinBox.NoButtons)
+ self.setRange(0, 100)
+ self.setSuffix(' %')
+ self.setValue(value)
+ self.setToolTip(title)
+ self.setStatusTip(self.toolTip())
+ self.setAlignment(Qt.AlignCenter)
+
+ def minimumSizeHint(self):
+ height = super(LightWidget, self).minimumSizeHint().height()
+ fm = QFontMetrics(self.font())
+ width = fm.width(str(self.maximum()))
+ return QSize(width, height)
+
+ def color(self):
+ if self.value() == 50:
+ return None
+
+ strength = int(self.value()/100 * 255 + 0.5)
+ return QColor(strength, strength, strength)
diff --git a/resources.qrc b/resources.qrc
index fd8b6c07..7479b9e3 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -27,6 +27,9 @@
resources/icons/zoom.png
resources/icons/zoom-in.png
resources/icons/zoom-out.png
+resources/icons/light_reset.png
+resources/icons/light_lighten.png
+resources/icons/light_darken.png
resources/icons/cancel.png
resources/icons/next.png
resources/icons/prev.png
diff --git a/resources/icons/light_brighten.svg b/resources/icons/light_brighten.svg
new file mode 100644
index 00000000..f257e184
--- /dev/null
+++ b/resources/icons/light_brighten.svg
@@ -0,0 +1,112 @@
+
+
+
+
diff --git a/resources/icons/light_darken.png b/resources/icons/light_darken.png
new file mode 100644
index 00000000..4fa5adb6
Binary files /dev/null and b/resources/icons/light_darken.png differ
diff --git a/resources/icons/light_darken.svg b/resources/icons/light_darken.svg
new file mode 100644
index 00000000..1002ca85
--- /dev/null
+++ b/resources/icons/light_darken.svg
@@ -0,0 +1,52 @@
+
+
+
+
diff --git a/resources/icons/light_lighten.png b/resources/icons/light_lighten.png
new file mode 100644
index 00000000..c8679b42
Binary files /dev/null and b/resources/icons/light_lighten.png differ
diff --git a/resources/icons/light_reset.png b/resources/icons/light_reset.png
new file mode 100644
index 00000000..225765d0
Binary files /dev/null and b/resources/icons/light_reset.png differ
diff --git a/resources/icons/light_reset.svg b/resources/icons/light_reset.svg
new file mode 100644
index 00000000..6aea0030
--- /dev/null
+++ b/resources/icons/light_reset.svg
@@ -0,0 +1,84 @@
+
+
+
+
diff --git a/resources/strings/strings.properties b/resources/strings/strings.properties
index 64601175..8c5d14ce 100644
--- a/resources/strings/strings.properties
+++ b/resources/strings/strings.properties
@@ -52,6 +52,13 @@ fitWin=Fit Window
fitWinDetail=Zoom follows window size
fitWidth=Fit Width
fitWidthDetail=Zoom follows window width
+lightbrighten=Brighten
+lightbrightenDetail=Increase Image Brightness
+lightdarken=Darken
+lightdarkenDetail=Decrease Image Brightness
+lightreset=Original Brightness
+lightresetDetail=Restore original brightness
+lightWidgetTitle=Image Brightness
editLabel=Edit Label
editLabelDetail=Modify the label of the selected Box
shapeLineColor=Shape Line Color