Assign different labels with different colors

This commit is contained in:
tzutalin 2017-10-23 16:27:40 +08:00
parent 713cf4537e
commit 6cf04adfa5
4 changed files with 118 additions and 50 deletions

View File

@ -27,7 +27,7 @@ except ImportError:
import resources import resources
# Add internal libs # Add internal libs
from libs.constants import * from libs.constants import *
from libs.lib import struct, newAction, newIcon, addActions, fmtShortcut from libs.lib import struct, newAction, newIcon, addActions, fmtShortcut, generateColorByText
from libs.settings import Settings from libs.settings import Settings
from libs.shape import Shape, DEFAULT_LINE_COLOR, DEFAULT_FILL_COLOR from libs.shape import Shape, DEFAULT_LINE_COLOR, DEFAULT_FILL_COLOR
from libs.canvas import Canvas from libs.canvas import Canvas
@ -209,13 +209,13 @@ class MainWindow(QMainWindow, WindowMixin):
open = action('&Open', self.openFile, open = action('&Open', self.openFile,
'Ctrl+O', 'open', u'Open image or label file') 'Ctrl+O', 'open', u'Open image or label file')
opendir = action('&Open Dir', self.openDir, opendir = action('&Open Dir', self.openDirDialog,
'Ctrl+u', 'open', u'Open Dir') 'Ctrl+u', 'open', u'Open Dir')
changeSavedir = action('&Change Save Dir', self.changeSavedir, changeSavedir = action('&Change Save Dir', self.changeSavedirDialog,
'Ctrl+r', 'open', u'Change default saved Annotation dir') 'Ctrl+r', 'open', u'Change default saved Annotation dir')
openAnnotation = action('&Open Annotation', self.openAnnotation, openAnnotation = action('&Open Annotation', self.openAnnotationDialog,
'Ctrl+Shift+O', 'open', u'Open Annotation') 'Ctrl+Shift+O', 'open', u'Open Annotation')
openNextImg = action('&Next Image', self.openNextImg, openNextImg = action('&Next Image', self.openNextImg,
@ -243,7 +243,7 @@ class MainWindow(QMainWindow, WindowMixin):
'Ctrl+Shift+L', 'color', u'Choose Box fill color') 'Ctrl+Shift+L', 'color', u'Choose Box fill color')
createMode = action('Create\nRectBox', self.setCreateMode, createMode = action('Create\nRectBox', self.setCreateMode,
'Ctrl+N', 'new', u'Start drawing Boxs', enabled=False) 'w', 'new', u'Start drawing Boxs', enabled=False)
editMode = action('&Edit\nRectBox', self.setEditMode, editMode = action('&Edit\nRectBox', self.setEditMode,
'Ctrl+J', 'edit', u'Move and edit Boxs', enabled=False) 'Ctrl+J', 'edit', u'Move and edit Boxs', enabled=False)
@ -427,13 +427,10 @@ class MainWindow(QMainWindow, WindowMixin):
(__appname__, self.defaultSaveDir)) (__appname__, self.defaultSaveDir))
self.statusBar().show() self.statusBar().show()
# or simply:
# self.restoreGeometry(settings[SETTING_WIN_GEOMETRY]
self.restoreState(settings.get(SETTING_WIN_STATE, QByteArray())) self.restoreState(settings.get(SETTING_WIN_STATE, QByteArray()))
self.lineColor = QColor(settings.get(SETTING_LINE_COLOR, Shape.line_color)) Shape.line_color = self.lineColor = QColor(settings.get(SETTING_LINE_COLOR, DEFAULT_LINE_COLOR))
self.fillColor = QColor(settings.get(SETTING_FILL_COLOR, Shape.fill_color)) Shape.fill_color = self.fillColor = QColor(settings.get(SETTING_FILL_COLOR, DEFAULT_FILL_COLOR))
Shape.line_color = self.lineColor self.canvas.setDrawingColor(self.lineColor)
Shape.fill_color = self.fillColor
# Add chris # Add chris
Shape.difficult = self.difficult Shape.difficult = self.difficult
@ -448,9 +445,12 @@ class MainWindow(QMainWindow, WindowMixin):
# Populate the File menu dynamically. # Populate the File menu dynamically.
self.updateFileMenu() self.updateFileMenu()
# Since loading the file may take some time, make sure it runs in the
# background. # Since loading the file may take some time, make sure it runs in the background.
self.queueEvent(partial(self.loadFile, self.filePath or "")) if self.filePath and os.path.isdir(self.filePath):
self.queueEvent(partial(self.importDirImages, self.filePath or ""))
elif self.filePath:
self.queueEvent(partial(self.loadFile, self.filePath or ""))
# Callbacks: # Callbacks:
self.zoomWidget.valueChanged.connect(self.paintCanvas) self.zoomWidget.valueChanged.connect(self.paintCanvas)
@ -598,10 +598,10 @@ class MainWindow(QMainWindow, WindowMixin):
def popLabelListMenu(self, point): def popLabelListMenu(self, point):
self.menus.labelList.exec_(self.labelList.mapToGlobal(point)) self.menus.labelList.exec_(self.labelList.mapToGlobal(point))
def editLabel(self, item=None): def editLabel(self):
if not self.canvas.editing(): if not self.canvas.editing():
return return
item = item if item else self.currentItem() item = self.currentItem()
text = self.labelDialog.popUp(item.text()) text = self.labelDialog.popUp(item.text())
if text is not None: if text is not None:
item.setText(text) item.setText(text)
@ -686,12 +686,18 @@ class MainWindow(QMainWindow, WindowMixin):
shape.difficult = difficult shape.difficult = difficult
shape.close() shape.close()
s.append(shape) s.append(shape)
self.addLabel(shape)
if line_color: if line_color:
shape.line_color = QColor(*line_color) shape.line_color = QColor(*line_color)
else:
shape.line_color = generateColorByText(label)
if fill_color: if fill_color:
shape.fill_color = QColor(*fill_color) shape.fill_color = QColor(*fill_color)
else:
shape.fill_color = generateColorByText(label)
self.addLabel(shape)
self.canvas.loadShapes(s) self.canvas.loadShapes(s)
@ -703,10 +709,8 @@ class MainWindow(QMainWindow, WindowMixin):
def format_shape(s): def format_shape(s):
return dict(label=s.label, return dict(label=s.label,
line_color=s.line_color.getRgb() line_color=s.line_color.getRgb(),
if s.line_color != self.lineColor else None, fill_color=s.fill_color.getRgb(),
fill_color=s.fill_color.getRgb()
if s.fill_color != self.fillColor else None,
points=[(p.x(), p.y()) for p in s.points], points=[(p.x(), p.y()) for p in s.points],
# add chris # add chris
difficult = s.difficult) difficult = s.difficult)
@ -723,8 +727,7 @@ class MainWindow(QMainWindow, WindowMixin):
self.lineColor.getRgb(), self.fillColor.getRgb()) self.lineColor.getRgb(), self.fillColor.getRgb())
return True return True
except LabelFileError as e: except LabelFileError as e:
self.errorMessage(u'Error saving label data', self.errorMessage(u'Error saving label data', u'<b>%s</b>' % e)
u'<b>%s</b>' % e)
return False return False
def copySelectedShape(self): def copySelectedShape(self):
@ -774,7 +777,9 @@ class MainWindow(QMainWindow, WindowMixin):
self.diffcButton.setChecked(False) self.diffcButton.setChecked(False)
if text is not None: if text is not None:
self.prevLabelText = text self.prevLabelText = text
self.addLabel(self.canvas.setLastLabel(text)) generate_color = generateColorByText(text)
shape = self.canvas.setLastLabel(text, generate_color, generate_color)
self.addLabel(shape)
if self.beginner(): # Switch to edit mode. if self.beginner(): # Switch to edit mode.
self.canvas.setEditing(True) self.canvas.setEditing(True)
self.actions.create.setEnabled(True) self.actions.create.setEnabled(True)
@ -904,6 +909,7 @@ class MainWindow(QMainWindow, WindowMixin):
# read data first and store for saving into label file. # read data first and store for saving into label file.
self.imageData = read(unicodeFilePath, None) self.imageData = read(unicodeFilePath, None)
self.labelFile = None self.labelFile = None
image = QImage.fromData(self.imageData) image = QImage.fromData(self.imageData)
if image.isNull(): if image.isNull():
self.errorMessage(u'Error opening file', self.errorMessage(u'Error opening file',
@ -996,12 +1002,12 @@ class MainWindow(QMainWindow, WindowMixin):
settings[SETTING_FILL_COLOR] = self.fillColor settings[SETTING_FILL_COLOR] = self.fillColor
settings[SETTING_RECENT_FILES] = self.recentFiles settings[SETTING_RECENT_FILES] = self.recentFiles
settings[SETTING_ADVANCE_MODE] = not self._beginner settings[SETTING_ADVANCE_MODE] = not self._beginner
if self.defaultSaveDir is not None and len(self.defaultSaveDir) > 1: if self.defaultSaveDir and os.path.exists(self.defaultSaveDir):
settings[SETTING_SAVE_DIR] = ustr(self.defaultSaveDir) settings[SETTING_SAVE_DIR] = ustr(self.defaultSaveDir)
else: else:
settings[SETTING_SAVE_DIR] = "" settings[SETTING_SAVE_DIR] = ""
if self.lastOpenDir is not None and len(self.lastOpenDir) > 1: if self.lastOpenDir and os.path.exists(self.lastOpenDir):
settings[SETTING_LAST_OPEN_DIR] = self.lastOpenDir settings[SETTING_LAST_OPEN_DIR] = self.lastOpenDir
else: else:
settings[SETTING_LAST_OPEN_DIR] = "" settings[SETTING_LAST_OPEN_DIR] = ""
@ -1028,7 +1034,7 @@ class MainWindow(QMainWindow, WindowMixin):
images.sort(key=lambda x: x.lower()) images.sort(key=lambda x: x.lower())
return images return images
def changeSavedir(self, _value=False): def changeSavedirDialog(self, _value=False):
if self.defaultSaveDir is not None: if self.defaultSaveDir is not None:
path = ustr(self.defaultSaveDir) path = ustr(self.defaultSaveDir)
else: else:
@ -1045,7 +1051,7 @@ class MainWindow(QMainWindow, WindowMixin):
('Change saved folder', self.defaultSaveDir)) ('Change saved folder', self.defaultSaveDir))
self.statusBar().show() self.statusBar().show()
def openAnnotation(self, _value=False): def openAnnotationDialog(self, _value=False):
if self.filePath is None: if self.filePath is None:
self.statusBar().showMessage('Please select image first') self.statusBar().showMessage('Please select image first')
self.statusBar().show() self.statusBar().show()
@ -1061,23 +1067,26 @@ class MainWindow(QMainWindow, WindowMixin):
filename = filename[0] filename = filename[0]
self.loadPascalXMLByFilename(filename) self.loadPascalXMLByFilename(filename)
def openDir(self, _value=False): def openDirDialog(self, _value=False):
if not self.mayContinue(): if not self.mayContinue():
return return
path = os.path.dirname(self.filePath)\ defaultOpenDirPath = '.'
if self.filePath else '.' if self.lastOpenDir and os.path.exists(self.lastOpenDir):
defaultOpenDirPath = self.lastOpenDir
if self.lastOpenDir is not None and len(self.lastOpenDir) > 1: else:
path = self.lastOpenDir defaultOpenDirPath = os.path.dirname(self.filePath) if self.filePath else '.'
dirpath = ustr(QFileDialog.getExistingDirectory(self, dirpath = ustr(QFileDialog.getExistingDirectory(self,
'%s - Open Directory' % __appname__, path, QFileDialog.ShowDirsOnly '%s - Open Directory' % __appname__, defaultOpenDirPath,
| QFileDialog.DontResolveSymlinks)) QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks))
self.importDirImages(dirpath)
if dirpath is not None and len(dirpath) > 1:
self.lastOpenDir = dirpath def importDirImages(self, dirpath):
if not self.mayContinue() or not dirpath:
return
self.lastOpenDir = dirpath
self.dirname = dirpath self.dirname = dirpath
self.filePath = None self.filePath = None
self.fileListWidget.clear() self.fileListWidget.clear()
@ -1109,7 +1118,7 @@ class MainWindow(QMainWindow, WindowMixin):
if self.dirty is True: if self.dirty is True:
self.saveFile() self.saveFile()
else: else:
self.changeSavedir() self.changeSavedirDialog()
return return
if not self.mayContinue(): if not self.mayContinue():
@ -1240,8 +1249,8 @@ class MainWindow(QMainWindow, WindowMixin):
default=DEFAULT_LINE_COLOR) default=DEFAULT_LINE_COLOR)
if color: if color:
self.lineColor = color self.lineColor = color
# Change the color for all shape lines: Shape.line_color = color
Shape.line_color = self.lineColor self.canvas.setDrawingColor(color)
self.canvas.update() self.canvas.update()
self.setDirty() self.setDirty()
@ -1292,7 +1301,7 @@ class MainWindow(QMainWindow, WindowMixin):
for line in f: for line in f:
line = line.strip() line = line.strip()
if self.labelHist is None: if self.labelHist is None:
self.lablHist = [line] self.labelHist = [line]
else: else:
self.labelHist.append(line) self.labelHist.append(line)

View File

@ -41,8 +41,9 @@ class Canvas(QWidget):
self.current = None self.current = None
self.selectedShape = None # save the selected shape here self.selectedShape = None # save the selected shape here
self.selectedShapeCopy = None self.selectedShapeCopy = None
self.lineColor = QColor(0, 0, 255) self.drawingLineColor = QColor(0, 0, 255)
self.line = Shape(line_color=self.lineColor) self.drawingRectColor = QColor(0, 0, 255)
self.line = Shape(line_color=self.drawingLineColor)
self.prevPoint = QPointF() self.prevPoint = QPointF()
self.offsets = QPointF(), QPointF() self.offsets = QPointF(), QPointF()
self.scale = 1.0 self.scale = 1.0
@ -61,6 +62,10 @@ class Canvas(QWidget):
self.setFocusPolicy(Qt.WheelFocus) self.setFocusPolicy(Qt.WheelFocus)
self.verified = False self.verified = False
def setDrawingColor(self, qColor):
self.drawingLineColor = qColor
self.drawingRectColor = qColor
def enterEvent(self, ev): def enterEvent(self, ev):
self.overrideCursor(self._cursor) self.overrideCursor(self._cursor)
@ -103,7 +108,7 @@ class Canvas(QWidget):
if self.drawing(): if self.drawing():
self.overrideCursor(CURSOR_DRAW) self.overrideCursor(CURSOR_DRAW)
if self.current: if self.current:
color = self.lineColor color = self.drawingLineColor
if self.outOfPixmap(pos): if self.outOfPixmap(pos):
# Don't allow the user to draw outside the pixmap. # Don't allow the user to draw outside the pixmap.
# Project the point to the pixmap's edges. # Project the point to the pixmap's edges.
@ -414,8 +419,7 @@ class Canvas(QWidget):
rightBottom = self.line[1] rightBottom = self.line[1]
rectWidth = rightBottom.x() - leftTop.x() rectWidth = rightBottom.x() - leftTop.x()
rectHeight = rightBottom.y() - leftTop.y() rectHeight = rightBottom.y() - leftTop.y()
color = QColor(0, 220, 0) p.setPen(self.drawingRectColor)
p.setPen(color)
brush = QBrush(Qt.BDiagPattern) brush = QBrush(Qt.BDiagPattern)
p.setBrush(brush) p.setBrush(brush)
p.drawRect(leftTop.x(), leftTop.y(), rectWidth, rectHeight) p.drawRect(leftTop.x(), leftTop.y(), rectWidth, rectHeight)
@ -606,9 +610,15 @@ class Canvas(QWidget):
points = [p1+p2 for p1, p2 in zip(self.selectedShape.points, [step]*4)] points = [p1+p2 for p1, p2 in zip(self.selectedShape.points, [step]*4)]
return True in map(self.outOfPixmap, points) return True in map(self.outOfPixmap, points)
def setLastLabel(self, text): def setLastLabel(self, text, line_color = None, fill_color = None):
assert text assert text
self.shapes[-1].label = text self.shapes[-1].label = text
if line_color:
self.shapes[-1].line_color = line_color
if fill_color:
self.shapes[-1].fill_color = fill_color
return self.shapes[-1] return self.shapes[-1]
def undoLastLine(self): def undoLastLine(self):

View File

@ -58,6 +58,35 @@ class LabelFile(object):
def toggleVerify(self): def toggleVerify(self):
self.verified = not self.verified self.verified = not self.verified
''' ttf is disable
def load(self, filename):
import json
with open(filename, 'rb') as f:
data = json.load(f)
imagePath = data['imagePath']
imageData = b64decode(data['imageData'])
lineColor = data['lineColor']
fillColor = data['fillColor']
shapes = ((s['label'], s['points'], s['line_color'], s['fill_color'])\
for s in data['shapes'])
# Only replace data after everything is loaded.
self.shapes = shapes
self.imagePath = imagePath
self.imageData = imageData
self.lineColor = lineColor
self.fillColor = fillColor
def save(self, filename, shapes, imagePath, imageData, lineColor=None, fillColor=None):
import json
with open(filename, 'wb') as f:
json.dump(dict(
shapes=shapes,
lineColor=lineColor, fillColor=fillColor,
imagePath=imagePath,
imageData=b64encode(imageData)),
f, ensure_ascii=True, indent=2)
'''
@staticmethod @staticmethod
def isLabelFile(filename): def isLabelFile(filename):
fileSuffix = os.path.splitext(filename)[1].lower() fileSuffix = os.path.splitext(filename)[1].lower()

View File

@ -1,5 +1,5 @@
from math import sqrt from math import sqrt
import hashlib
try: try:
from PyQt5.QtGui import * from PyQt5.QtGui import *
from PyQt5.QtCore import * from PyQt5.QtCore import *
@ -71,3 +71,23 @@ def distance(p):
def fmtShortcut(text): def fmtShortcut(text):
mod, key = text.split('+', 1) mod, key = text.split('+', 1)
return '<b>%s</b>+<b>%s</b>' % (mod, key) return '<b>%s</b>+<b>%s</b>' % (mod, key)
def generateColorByText(text):
color_table = []
color_table.append(QColor(0, 0, 50))
color_table.append(QColor(0, 0, 255))
color_table.append(QColor(0, 50, 0))
color_table.append(QColor(0, 255, 0))
color_table.append(QColor(50, 0, 0))
color_table.append(QColor(255, 0, 0))
color_table.append(QColor(0, 50, 50))
color_table.append(QColor(0, 255, 255))
color_table.append(QColor(50, 50, 0))
color_table.append(QColor(255, 255, 0))
color_table.append(QColor(50, 0, 50))
color_table.append(QColor(255, 0, 255))
color_table.append(QColor(50, 50, 50))
color_table.append(QColor(255, 255, 255))
colorInd = int(hashlib.sha1(text.encode('utf-8')).hexdigest(), 16) % len(color_table)
return color_table[colorInd]