class TsinhuaDaimlerViewer(QtGui.QMainWindow):

    #############################
    ## Construction / Destruction
    #############################

    # Constructor
    def __init__(self):
        # Construct base class
        super(TsinhuaDaimlerViewer, self).__init__()

        # This is the configuration.

        # The filename of the image we currently working on
        self.currentFile = ""
        # The filename of the labels we currently working on
        self.currentLabelFile = ""
        # Folder name of the dataset
        self.datasetFolderName = ""
        # Folder name of the dataset
        self.disparityFolderName = ""
        # The name of the currently loaded datasetType
        self.datasetType = ""
        # The path of the labels.
        # Within these folders folder we expect the label with a filename matching
        # the images, except for the extension
        self.labelPath = dataRootPath + "/labelData/"
        # The transparency of the labels over the image
        self.transp = 0.5
        # The zoom toggle
        self.zoom = False
        # The zoom factor
        self.zoomFactor = 1.5
        # The size of the zoom window. Currently there is no setter or getter for that
        self.zoomSize = 400  #px

        self.disp8bitToggle = False

        # The width that we actually use to show the image
        self.w = 0
        # The height that we actually use to show the image
        self.h = 0
        # The horizontal offset where we start drawing within the widget
        self.xoff = 0
        # The vertical offset where we start drawing withing the widget
        self.yoff = 0
        # A gap that we  leave around the image as little border
        self.bordergap = 20
        # The scale that was used, ie
        # self.w = self.scale * self.image.width()
        # self.h = self.scale * self.image.height()
        self.scale = 1.0
        # Filenames of all images in current folder
        self.images = []
        # Image extension
        self.imageExt = "_leftImg8bit.png"
        self.dispExt = "_disparity.png"
        self.gtExt = "_labelData.json"
        # Current image as QImage
        self.image = QtGui.QImage()
        # Index of the current image
        self.idx = 0
        # All annotated objects in current image, i.e. list of labelObject
        self.annotation = []
        # The current object the mouse points to. It's index in self.labels
        self.mouseObj = -1
        # The object that is highlighted and its label. An object instance
        self.highlightObj = None
        self.highlightObjLabel = None
        # The position of the mouse
        self.mousePosOrig = None
        # The position of the mouse scaled to label coordinates
        self.mousePosScaled = None
        # If the mouse is outside of the image
        self.mouseOutsideImage = True
        # The position of the mouse upon enabling the zoom window
        self.mousePosOnZoom = None
        # A list of toolbar actions that need an image
        self.actImage = []
        # A list of toolbar actions that need an image that is not the first
        self.actImageNotFirst = []
        # A list of toolbar actions that need an image that is not the last
        self.actImageNotLast = []
        # Toggle status of the play icon
        self.playState = False

        # Default label
        self.defaultLabel = 'pedestrian'
        # Last selected label
        self.lastLabel = self.defaultLabel

        # Setup the GUI
        self.initUI()

        # load it
        self.loadDataset()
        self.imageChanged()

    # Destructor
    def __del__(self):
        return

    # Construct everything GUI related. Called by constructor
    def initUI(self):
        # Create a toolbar
        self.toolbar = self.addToolBar('Tools')

        # Add the tool buttons
        iconDir = os.path.join(os.path.dirname(sys.argv[0]), 'icons')

        # Loading
        loadAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'open.png')), '&Tools', self)
        loadAction.setShortcuts(['o'])
        self.setTip(loadAction, 'Open datset')
        loadAction.triggered.connect(self.getDatasetTypeFromUser)
        self.toolbar.addAction(loadAction)

        # Open previous image
        backAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'back.png')), '&Tools', self)
        backAction.setShortcut('left')
        backAction.setStatusTip('Previous image')
        backAction.triggered.connect(self.prevImage)
        self.toolbar.addAction(backAction)
        self.actImageNotFirst.append(backAction)

        # Open next image
        nextAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'next.png')), '&Tools', self)
        nextAction.setShortcut('right')
        self.setTip(nextAction, 'Next image')
        nextAction.triggered.connect(self.nextImage)
        self.toolbar.addAction(nextAction)
        self.actImageNotLast.append(nextAction)

        # Play
        playAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'play.png')), '&Tools', self)
        playAction.setShortcut(' ')
        playAction.setCheckable(True)
        playAction.setChecked(False)
        self.setTip(playAction, 'Play all images')
        playAction.triggered.connect(self.playImages)
        self.toolbar.addAction(playAction)
        self.actImageNotLast.append(playAction)
        self.playAction = playAction

        # Select image
        selImageAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'shuffle.png')), '&Tools', self)
        selImageAction.setShortcut('i')
        self.setTip(selImageAction, 'Select image')
        selImageAction.triggered.connect(self.selectImage)
        self.toolbar.addAction(selImageAction)
        self.actImage.append(selImageAction)

        # Enable/disable zoom. Toggle button
        zoomAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'zoom.png')), '&Tools', self)
        zoomAction.setShortcuts(['z'])
        zoomAction.setCheckable(True)
        zoomAction.setChecked(self.zoom)
        self.setTip(zoomAction, 'Enable/disable permanent zoom')
        zoomAction.toggled.connect(self.zoomToggle)
        self.toolbar.addAction(zoomAction)
        self.actImage.append(zoomAction)

        # Decrease transparency
        minusAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'minus.png')), '&Tools', self)
        minusAction.setShortcut('-')
        self.setTip(minusAction, 'Decrease transparency')
        minusAction.triggered.connect(self.minus)
        self.toolbar.addAction(minusAction)

        # Increase transparency
        plusAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'plus.png')), '&Tools', self)
        plusAction.setShortcut('+')
        self.setTip(plusAction, 'Increase transparency')
        plusAction.triggered.connect(self.plus)
        self.toolbar.addAction(plusAction)

        # Display path to current image in message bar
        displayFilepathAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'filepath.png')), '&Tools', self)
        displayFilepathAction.setShortcut('f')
        self.setTip(displayFilepathAction, 'Show path to current image')
        displayFilepathAction.triggered.connect(self.displayFilepath)
        self.toolbar.addAction(displayFilepathAction)

        # Toggle disp/8bit view
        toggleDisp8bit = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'layer.png')), '&Tools', self)
        toggleDisp8bit.setShortcut('d')
        self.setTip(toggleDisp8bit, 'Toggle between disparity and 8bit image')
        toggleDisp8bit.triggered.connect(self.toggleDisp8bitImage)
        self.toolbar.addAction(toggleDisp8bit)

        # Display help message
        helpAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'comment.png')), '&Tools', self)
        helpAction.setShortcut('h')
        self.setTip(helpAction, 'Help')
        helpAction.triggered.connect(self.displayHelpMessage)
        self.toolbar.addAction(helpAction)

        # Close the application
        exitAction = QtGui.QAction(
            QtGui.QIcon(os.path.join(iconDir, 'exit.png')), '&Tools', self)
        exitAction.setShortcuts(['Esc'])
        self.setTip(exitAction, 'Exit')
        exitAction.triggered.connect(self.close)
        self.toolbar.addAction(exitAction)

        # The default text for the status bar
        self.defaultStatusbar = 'Ready'
        # Create a statusbar. Init with default
        self.statusBar().showMessage(self.defaultStatusbar)

        # Enable mouse move events
        self.setMouseTracking(True)
        self.toolbar.setMouseTracking(True)
        # Open in full screen
        #self.showFullScreen( )
        # Set a title
        self.applicationTitle = 'Tsinghua-Daimler Dataset Viewer v0.1'
        self.setWindowTitle(self.applicationTitle)
        self.displayHelpMessage()
        self.getDatasetTypeFromUser()
        # And show the application
        self.show()

    #############################
    ## Toolbar call-backs
    #############################

    # Switch to previous image in file list
    # Load the image
    # Load its labels
    # Update the mouse selection
    # View
    def prevImage(self):
        if not self.images:
            return
        if self.idx > 0:
            self.idx -= 1
            self.imageChanged()
        else:
            message = "Already at the first image"
            self.statusBar().showMessage(message)
        return

    # Switch to next image in file list
    # Load the image
    # Load its labels
    # Update the mouse selection
    # View
    def nextImage(self):
        if not self.images:
            return
        if self.idx < len(self.images) - 1:
            self.idx += 1
            self.imageChanged()
        elif self.playState:
            self.playState = False
            self.playAction.setChecked(False)
        else:
            message = "Already at the last image"
            self.statusBar().showMessage(message)
        if self.playState:
            QtCore.QTimer.singleShot(0, self.nextImage)
        return

    # Play images, i.e. auto-switch to next image
    def playImages(self, status):
        self.playState = status
        if self.playState:
            QtCore.QTimer.singleShot(0, self.nextImage)

    # Switch to a selected image of the file list
    # Ask the user for an image
    # Load the image
    # Load its labels
    # Update the mouse selection
    # View
    def selectImage(self):
        if not self.images:
            return

        dlgTitle = "Select image to load"
        self.statusBar().showMessage(dlgTitle)
        items = QtCore.QStringList([os.path.basename(i) for i in self.images])
        (item, ok) = QtGui.QInputDialog.getItem(self, dlgTitle, "Image", items,
                                                self.idx, False)
        if (ok and item):
            idx = items.indexOf(item)
            if idx != self.idx:
                self.idx = idx
                self.imageChanged()
        else:
            # Restore the message
            self.statusBar().showMessage(self.defaultStatusbar)

    # Toggle zoom
    def zoomToggle(self, status):
        self.zoom = status
        if status:
            self.mousePosOnZoom = self.mousePosOrig
        self.update()

    # Increase label transparency
    def minus(self):
        self.transp = max(self.transp - 0.1, 0.0)
        self.update()

    def displayFilepath(self):
        self.statusBar().showMessage("Current image: {0}".format(
            self.currentFile))
        self.update()

    def displayHelpMessage(self):

        message = \
        self.applicationTitle + "\n\n" +\
        "INSTRUCTIONS\n" +\
        " - select a dataset type from drop-down menu\n" +\
        " - browse images and labels using\n" +\
        "   the toolbar buttons or the controls below\n" +\
        "\n" +\
        "CONTROLS\n" +\
        " - select dataset type [o]\n" +\
        " - highlight objects [move mouse]\n" +\
        " - next image [left arrow]\n" +\
        " - previous image [right arrow]\n" +\
        " - toggle autoplay [space]\n" +\
        " - increase/decrease label transparency\n" +\
        "   [ctrl+mousewheel] or [+ / -]\n" +\
        " - open zoom window [z]\n" +\
        "       zoom in/out [mousewheel]\n" +\
        "       enlarge/shrink zoom window [shift+mousewheel]\n" +\
        " - select a specific image [i]\n" +\
        " - show path to image below [f]\n" +\
 " - toggle between colorImage/disparity [d]\n" +\
        " - exit viewer [esc]\n"

        QtGui.QMessageBox.about(self, "HELP!", message)
        self.update()

    # Decrease label transparency
    def plus(self):
        self.transp = min(self.transp + 0.1, 1.0)
        self.update()

    # Close the application
    def closeEvent(self, event):
        event.accept()

    def toggleDisp8bitImage(self):
        self.disp8bitToggle = not self.disp8bitToggle
        self.imageChanged()

    #############################
    ## Custom events
    #############################

    def imageChanged(self):
        # Load the first image
        self.loadImage()
        # Load its labels if available
        self.loadLabels()
        # Update the object the mouse points to
        self.updateMouseObject()
        # Update the GUI
        self.update()

    #############################
    ## File I/O
    #############################

    # Load the currently selected dataset type if possible
    def loadDataset(self):
        # Search for all *.pngs to get the image list
        self.images = []
        if os.path.isdir(self.datasetFolderName):
            self.images = glob.glob(self.datasetFolderName + '/*' +
                                    self.imageExt)
            self.images.sort()

        self.disparities = []
        if os.path.isdir(self.disparityFolderName):
            self.disparities = glob.glob(self.disparityFolderName + '/*' +
                                         self.dispExt)
            self.disparities.sort()

            if self.currentFile in self.images:
                self.idx = self.images.index(self.currentFile)
            else:
                self.idx = 0

    # Load the currently selected image
    # Does only load if not previously loaded
    # Does not refresh the GUI
    def loadImage(self):
        success = False
        message = self.defaultStatusbar
        if self.images:
            if (self.disp8bitToggle):
                filename = self.disparities[self.idx]
            else:
                filename = self.images[self.idx]

            filename = os.path.normpath(filename)
            if not self.image.isNull() and filename == self.currentFile:
                success = True
            else:
                self.image = QtGui.QImage(filename)
                if self.image.isNull():
                    message = "Failed to read image: {0}".format(filename)
                    print(message)
                    sys.exit()
                else:
                    message = "Read image: {0}".format(filename)
                    self.currentFile = filename
                    success = True

                #filenameDisp = os.path.normpath( self.disparities[self.idx] )
                #self.filenameDisp = QtGui.QImage(filenameDisp)
                #if self.filenameDisp.isNull():
                #    message = "Failed to read image: {0}".format( filenameDisp )

#   print(message)
#    sys.exit()
#else:
#    message = "Read image: {0}".format( filenameDisp )

# Update toolbar actions that need an image
        for act in self.actImage:
            act.setEnabled(success)
        for act in self.actImageNotFirst:
            act.setEnabled(success and self.idx > 0)
        for act in self.actImageNotLast:
            act.setEnabled(success and self.idx < len(self.images) - 1)

        self.statusBar().showMessage(message)

    # Load the labels from file
    # Only loads if they exist
    # Otherwise the filename is stored and that's it
    def loadLabels(self):
        filename = self.getLabelFilename()
        if not filename:
            self.clearAnnotation()
            return

        # If we have everything and the filename did not change, then we are good
        if self.annotation and filename == self.currentLabelFile:
            return

        # Clear the current labels first
        self.clearAnnotation()

        try:
            self.annotation = Annotation()
            self.annotation.fromJsonFile(filename)
        except IOError as e:
            # This is the error if the file does not exist
            message = "Error parsing labels in {0}. Message: {1}".format(
                filename, e.strerror)
            self.statusBar().showMessage(message)

        # Remember the filename loaded
        self.currentLabelFile = filename

        # Remeber the status bar message to restore it later
        restoreMessage = self.statusBar().currentMessage()

        # Restore the message
        self.statusBar().showMessage(restoreMessage)

    #############################
    ## Drawing
    #############################

    # This method is called when redrawing everything
    # Can be manually triggered by self.update()
    # Note that there must not be any other self.update within this method
    # or any methods that are called within
    def paintEvent(self, event):
        # Create a QPainter that can perform draw actions within a widget or image
        qp = QtGui.QPainter()
        # Begin drawing in the application widget
        qp.begin(self)
        # Update scale
        self.updateScale(qp)
        # Determine the object ID to highlight
        self.getHighlightedObject(qp)
        # Draw the image first
        self.drawImage(qp)
        # Draw the labels on top
        overlay = self.drawLabels(qp)
        # Draw the label name next to the mouse
        self.drawLabelAtMouse(qp)
        # Draw the zoom
        self.drawZoom(qp, overlay)

        # Thats all drawing
        qp.end()

        # Forward the paint event
        QtGui.QMainWindow.paintEvent(self, event)

    # Update the scaling
    def updateScale(self, qp):
        if not self.image.width() or not self.image.height():
            return
        # Horizontal offset
        self.xoff = self.bordergap
        # Vertical offset
        self.yoff = self.toolbar.height() + self.bordergap
        # We want to make sure to keep the image aspect ratio and to make it fit within the widget
        # Without keeping the aspect ratio, each side of the image is scaled (multiplied) with
        sx = float(qp.device().width() - 2 * self.xoff) / self.image.width()
        sy = float(qp.device().height() - 2 * self.yoff) / self.image.height()
        # To keep the aspect ratio while making sure it fits, we use the minimum of both scales
        # Remember the scale for later
        self.scale = min(sx, sy)
        # These are then the actual dimensions used
        self.w = self.scale * self.image.width()
        self.h = self.scale * self.image.height()

    # Determine the highlighted object for drawing
    def getHighlightedObject(self, qp):
        # This variable we want to fill
        self.highlightObj = None

        # Without labels we cannot do so
        if not self.annotation:
            return

        # If available its the selected object
        highlightObjId = -1
        # If not available but the polygon is empty or closed, its the mouse object
        if highlightObjId < 0 and not self.mouseOutsideImage:
            highlightObjId = self.mouseObj
        # Get the actual object that is highlighted
        if highlightObjId >= 0:
            self.highlightObj = self.annotation.getLinearObjects(
            )[highlightObjId]
            self.highlightObjLabel = self.annotation.getLinearObjects(
            )[highlightObjId].uniqueId

    # Draw the image in the given QPainter qp
    def drawImage(self, qp):
        # Return if no image available
        if self.image.isNull():
            return

        # Save the painters current setting to a stack
        qp.save()
        # Draw the image
        qp.drawImage(QtCore.QRect(self.xoff, self.yoff, self.w, self.h),
                     self.image)
        #qp.drawImage(QtCore.QRect( self.xoff + self.w/2, self.yoff, self.w/2, self.h/2 ), self.filenameDisp)
        # Restore the saved setting from the stack
        qp.restore()

    def getPolygon(self, obj):
        poly = QtGui.QPolygonF()
        point0 = QtCore.QPointF(obj.boundingBoxLabel.p0.x,
                                obj.boundingBoxLabel.p0.y)
        point1 = QtCore.QPointF(obj.boundingBoxLabel.p1.x,
                                obj.boundingBoxLabel.p0.y)
        point2 = QtCore.QPointF(obj.boundingBoxLabel.p1.x,
                                obj.boundingBoxLabel.p1.y)
        point3 = QtCore.QPointF(obj.boundingBoxLabel.p0.x,
                                obj.boundingBoxLabel.p1.y)
        poly.append(point0)
        poly.append(point1)
        poly.append(point2)
        poly.append(point3)
        return poly

    # Draw the labels in the given QPainter qp
    # optionally provide a list of labels to ignore
    def drawLabels(self, qp, ignore=[]):
        if self.image.isNull() or self.w == 0 or self.h == 0:
            return
        if not self.annotation:
            return

        # The overlay is created in the viewing coordinates
        # This way, the drawing is more dense and the polygon edges are nicer
        # We create an image that is the overlay
        # Within this image we draw using another QPainter
        # Finally we use the real QPainter to overlay the overlay-image on what is drawn so far

        # The image that is used to draw the overlays
        overlay = QtGui.QImage(self.w, self.h,
                               QtGui.QImage.Format_ARGB32_Premultiplied)

        #sanity check if image is loaded
        eng = overlay.paintEngine()
        if (not eng):
            return

        # delete all labels from previous frame
        overlay.fill(QtCore.Qt.transparent)

        # Create a new QPainter that draws in the overlay image
        qp2 = QtGui.QPainter()
        qp2.begin(overlay)

        # The color of the outlines
        qp2.setPen(QtGui.QColor('white'))
        # Draw all objects
        for obj in self.annotation.getLinearObjects():

            poly = self.getPolygon(obj)

            # Scale the polygon properly
            polyToDraw = poly * QtGui.QTransform.fromScale(
                self.scale, self.scale)

            # Default drawing
            # Color from color table, solid brush
            try:
                col = QtGui.QColor(*name2label[obj.classId].color)
            except:
                print obj.classId
                col = (0, 0, 0)

            brush = QtGui.QBrush(col, QtCore.Qt.SolidPattern)
            qp2.setBrush(brush)
            # Overwrite drawing if this is the highlighted object
            if self.highlightObj and obj == self.highlightObj:
                # First clear everything below of the polygon
                qp2.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
                qp2.drawPolygon(polyToDraw)
                qp2.setCompositionMode(
                    QtGui.QPainter.CompositionMode_SourceOver)
                # Set the drawing to a special pattern
                brush = QtGui.QBrush(col, QtCore.Qt.DiagCrossPattern)
                qp2.setBrush(brush)

            qp2.drawPolygon(polyToDraw)

        # Draw outline of selected object dotted
        if self.highlightObj:
            brush = QtGui.QBrush(QtCore.Qt.NoBrush)
            qp2.setBrush(brush)
            qp2.setPen(QtCore.Qt.DashLine)
            polyToDraw = self.getPolygon(
                self.highlightObj) * QtGui.QTransform.fromScale(
                    self.scale, self.scale)
            qp2.drawPolygon(polyToDraw)

        # End the drawing of the overlay
        qp2.end()
        # Save QPainter settings to stack
        qp.save()
        # Define transparency
        qp.setOpacity(self.transp)
        # Draw the overlay image
        qp.drawImage(self.xoff, self.yoff, overlay)
        # Restore settings
        qp.restore()

        return overlay

    # Draw the label name next to the mouse
    def drawLabelAtMouse(self, qp):
        # Nothing to do without a highlighted object
        if not self.highlightObj:
            return
        # Nothing to without a mouse position
        if not self.mousePosOrig:
            return

        # Save QPainter settings to stack
        qp.save()

        # That is the mouse positiong
        mouse = self.mousePosOrig

        # Will show zoom
        showZoom = self.zoom and not self.image.isNull() and self.w and self.h

        # The text that is written next to the mouse
        mouseText = self.highlightObj.uniqueId

        # Where to write the text
        # Depends on the zoom (additional offset to mouse to make space for zoom?)
        # The location in the image (if we are at the top we want to write below of the mouse)
        off = 33
        if showZoom:
            off += self.zoomSize / 2
        if mouse.y() - off > self.toolbar.height():
            top = mouse.y() - off
            btm = mouse.y()
            vAlign = QtCore.Qt.AlignTop
        else:
            # The height of the cursor
            if not showZoom:
                off += 20
            top = mouse.y()
            btm = mouse.y() + off
            vAlign = QtCore.Qt.AlignBottom

        # Here we can draw
        rect = QtCore.QRect()
        rect.setTopLeft(QtCore.QPoint(mouse.x() - 200, top))
        rect.setBottomRight(QtCore.QPoint(mouse.x() + 200, btm))

        # The font to use
        font = QtGui.QFont("Helvetica", 20, QtGui.QFont.Bold)
        qp.setFont(font)
        # Non-transparent
        qp.setOpacity(1)
        # Draw the text, horizontally centered
        #        qp.drawText(rect,QtCore.Qt.AlignHCenter|vAlign,mouseText)
        # Restore settings
        qp.restore()

    # Draw the zoom
    def drawZoom(self, qp, overlay):
        # Zoom disabled?
        if not self.zoom:
            return
        # No image
        if self.image.isNull() or not self.w or not self.h:
            return
        # No mouse
        if not self.mousePosOrig:
            return

        # Abbrevation for the zoom window size
        zoomSize = self.zoomSize
        # Abbrevation for the mouse position
        mouse = self.mousePosOrig

        # The pixel that is the zoom center
        pix = self.mousePosScaled
        # The size of the part of the image that is drawn in the zoom window
        selSize = zoomSize / (self.zoomFactor * self.zoomFactor)
        # The selection window for the image
        sel = QtCore.QRectF(pix.x() - selSize / 2,
                            pix.y() - selSize / 2, selSize, selSize)
        # The selection window for the widget
        view = QtCore.QRectF(mouse.x() - zoomSize / 2,
                             mouse.y() - zoomSize / 2, zoomSize, zoomSize)
        if overlay:
            overlay_scaled = overlay.scaled(self.image.width(),
                                            self.image.height())
        else:
            overlay_scaled = QtGui.QImage(
                self.image.width(), self.image.height(),
                QtGui.QImage.Format_ARGB32_Premultiplied)

        # Show the zoom image
        qp.save()
        qp.drawImage(view, self.image, sel)
        qp.setOpacity(self.transp)
        qp.drawImage(view, overlay_scaled, sel)
        qp.restore()

    #############################
    ## Mouse/keyboard events
    #############################

    # Mouse moved
    # Need to save the mouse position
    # Need to drag a polygon point
    # Need to update the mouse selected object
    def mouseMoveEvent(self, event):
        if self.image.isNull() or self.w == 0 or self.h == 0:
            return

        mousePosOrig = QtCore.QPointF(event.x(), event.y())
        mousePosScaled = QtCore.QPointF(
            float(mousePosOrig.x() - self.xoff) / self.scale,
            float(mousePosOrig.y() - self.yoff) / self.scale)
        mouseOutsideImage = not self.image.rect().contains(
            mousePosScaled.toPoint())

        mousePosScaled.setX(max(mousePosScaled.x(), 0.))
        mousePosScaled.setY(max(mousePosScaled.y(), 0.))
        mousePosScaled.setX(min(mousePosScaled.x(), self.image.rect().right()))
        mousePosScaled.setY(min(mousePosScaled.y(),
                                self.image.rect().bottom()))

        if not self.image.rect().contains(mousePosScaled.toPoint()):
            print self.image.rect()
            print mousePosScaled.toPoint()
            self.mousePosScaled = None
            self.mousePosOrig = None
            self.updateMouseObject()
            self.update()
            return

        self.mousePosScaled = mousePosScaled
        self.mousePosOrig = mousePosOrig
        self.mouseOutsideImage = mouseOutsideImage

        # Redraw
        self.updateMouseObject()
        self.update()

    # Mouse left the widget
    def leaveEvent(self, event):
        self.mousePosOrig = None
        self.mousePosScaled = None
        self.mouseOutsideImage = True

    # Mouse wheel scrolled
    def wheelEvent(self, event):
        ctrlPressed = event.modifiers() & QtCore.Qt.ControlModifier

        deltaDegree = event.delta() / 8  # Rotation in degree
        deltaSteps = deltaDegree / 15  # Usually one step on the mouse is 15 degrees

        if ctrlPressed:
            self.transp = max(min(self.transp + (deltaSteps * 0.1), 1.0), 0.0)
            self.update()
        else:
            if self.zoom:
                # If shift is pressed, change zoom window size
                if event.modifiers() and QtCore.Qt.Key_Shift:
                    self.zoomSize += deltaSteps * 10
                    self.zoomSize = max(self.zoomSize, 10)
                    self.zoomSize = min(self.zoomSize, 1000)
                # Change zoom factor
                else:
                    self.zoomFactor += deltaSteps * 0.05
                    self.zoomFactor = max(self.zoomFactor, 0.1)
                    self.zoomFactor = min(self.zoomFactor, 10)
                self.update()

    #############################
    ## Little helper methods
    #############################

    # Helper method that sets tooltip and statustip
    # Provide an QAction and the tip text
    # This text is appended with a hotkeys and then assigned
    def setTip(self, action, tip):
        tip += " (Hotkeys: '" + "', '".join(
            [str(s.toString()) for s in action.shortcuts()]) + "')"
        action.setStatusTip(tip)
        action.setToolTip(tip)

    # Update the object that is selected by the current mouse curser
    def updateMouseObject(self):
        self.mouseObj = -1
        if self.mousePosScaled is None:
            return
        for idx in reversed(range(len(self.annotation.getLinearObjects()))):
            obj = self.annotation.getLinearObjects()[idx]
            obj = self.annotation.getLinearObjects()[idx]
            if self.getPolygon(obj).containsPoint(self.mousePosScaled,
                                                  QtCore.Qt.OddEvenFill):
                self.mouseObj = idx
                break

    # Clear the current labels
    def clearAnnotation(self):
        self.annotation = None
        self.currentLabelFile = ""

    def getDatasetTypeFromUser(self):
        # Reset the status bar to this message when leaving
        restoreMessage = self.statusBar().currentMessage()

        img_folder = os.path.join(dataRootPath, 'leftImg8bit')
        disp_folder = os.path.join(dataRootPath, 'disparity')
        label_folder = os.path.join(dataRootPath, 'labelData')
        labeled_cities = []
        data_dirs = ['valid', 'train', 'test', 'NonVRU']
        for d in data_dirs:
            cities = glob.glob(os.path.join(img_folder, d, '*'))
            cities.sort()
            labeled_cities.extend([(os.path.basename(c), d) for c in cities])

        # List of possible labels
        items = [d + ": " + c for (c, d) in labeled_cities]

        # Specify title
        dlgTitle = "Select dataset ['valid', 'train', 'test', 'NonVRU']"
        message = dlgTitle
        question = dlgTitle
        message = "Select dataset for viewing"
        question = "Which dataset would you like to view?"
        self.statusBar().showMessage(message)

        # Create and wait for dialog
        (datasetType, ok) = QtGui.QInputDialog.getItem(self, dlgTitle,
                                                       question, items, 0,
                                                       False)

        # Restore message
        self.statusBar().showMessage(restoreMessage)

        if ok and datasetType:
            self.datasetType = datasetType
            self.transp = 0.5
            self.datasetFolderName = os.path.normpath(
                os.path.join(img_folder, str(datasetType.split(": ")[0]),
                             str(datasetType.split(": ")[1])))
            self.disparityFolderName = os.path.normpath(
                os.path.join(disp_folder, str(datasetType.split(": ")[0]),
                             str(datasetType.split(": ")[1])))
            self.labelPath = os.path.normpath(
                os.path.join(label_folder, str(datasetType.split(": ")[0]),
                             str(datasetType.split(": ")[1])))
        self.loadDataset()
        self.imageChanged()

        return

    # Determine if the given candidate for a label path makes sense
    def isLabelPathValid(self, labelPath):
        return os.path.isdir(labelPath)

    # Get the filename where to load/save labels
    # Returns empty string if not possible
    # Set the createDirs to true, if you want to create needed directories
    def getLabelFilename(self, createDirs=False):
        # We need the name of the current dataset selected
        if not self.datasetType:
            return ""
        # And we need to have a directory where labels should be searched
        if not self.labelPath:
            return ""
        # Without the name of the current images, there is also nothing we can do
        if not self.currentFile:
            return ""
        # Check if the label directory is valid. This folder is selected by the user
        # and thus expected to exist
        if not self.isLabelPathValid(self.labelPath):
            return ""

        # Folder where to store the labels
        labelDir = os.path.join(self.labelPath)

        # If the folder does not exist, create it if allowed
        if not os.path.isdir(labelDir):
            if createDirs:
                os.makedirs(labelDir)
                if not os.path.isdir(labelDir):
                    return ""
            else:
                return ""
        # Generate the filename of the label file
        filename = os.path.basename(self.currentFile)
        filename = filename.replace(self.dispExt, self.imageExt)
        filename = filename.replace(self.imageExt, self.gtExt)
        filename = os.path.join(labelDir, filename)
        filename = os.path.normpath(filename)
        return filename

    # Disable the popup menu on right click
    def createPopupMenu(self):
        pass