def _drawMatch(img, roi1, roi2, yoffset): def _getCenter(roi): return int(0.5 * roi[1] + 0.5 * roi[3]), int(0.25 * roi[0] + 0.75 * roi[2]) roi2[0] += yoffset roi2[2] += yoffset util.drawScoredRoi(img, roi1) util.drawScoredRoi(img, roi2) center1 = _getCenter(roi1) center2 = _getCenter(roi2) cv2.line(img, center1, center2, [0, 0, 255], thickness=2)
def labelMatches(c, args): cv2.namedWindow("labelMatches") cv2.setMouseCallback('labelMatches', _monitorPressRelease) global mousePressed, mouseReleased, xpress, ypress c.execute('SELECT imagefile FROM images WHERE %s' % args.where_image) image_entries = c.fetchall() logging.debug('Found %d images' % len(image_entries)) if len(image_entries) < 2: logging.error('Found only %d images. Quit.' % len(image_entries)) return imreader = backendMedia.MediaReader(rootdir=args.rootdir) # For parsing keys. key_reader = KeyReader(args.key_dict) index_image = 1 action = None while action != 'exit' and index_image < len(image_entries): (imagefile1, ) = image_entries[index_image - 1] (imagefile2, ) = image_entries[index_image] img1 = imreader.imread(imagefile1) img2 = imreader.imread(imagefile2) # offset of the 2nd image, when they are stacked yoffset = img1.shape[0] # Make sure images have the same width, so that we can stack them. # That will happen when the video changes, so we'll skip that pair. if img1.shape[1] != img2.shape[1]: logging.warning('Skipping image pair "%s" and "%s" ' 'since they are of different width.' % (imagefile1, imagefile2)) index_image += 1 # get objects from both images c.execute('SELECT * FROM objects WHERE imagefile=? ', (imagefile1, )) objects1 = c.fetchall() logging.info('%d objects found for %s' % (len(objects1), imagefile1)) c.execute('SELECT * FROM objects WHERE imagefile=? ', (imagefile2, )) objects2 = c.fetchall() logging.info('%d objects found for %s' % (len(objects2), imagefile2)) # draw cars in both images for object_ in objects1: util.drawScoredRoi(img1, backendDb.objectField(object_, 'roi')) for object_ in objects2: util.drawScoredRoi(img2, backendDb.objectField(object_, 'roi')) i1 = i2 = None # Matches selected with a mouse. selectedMatch = None needRedraw = True action = None # Stay in side the loop until a key is pressed. while action is None: img_stack = np.vstack((img1, img2)) if needRedraw: # find existing matches, and make a map matchesOf1 = {} matchesOf2 = {} for j1 in range(len(objects1)): object1 = objects1[j1] for j2 in range(len(objects2)): object2 = objects2[j2] c.execute( 'SELECT match FROM matches WHERE objectid = ? INTERSECT ' 'SELECT match FROM matches WHERE objectid = ?', (backendDb.objectField(object1, 'objectid'), backendDb.objectField(object2, 'objectid'))) matches = c.fetchall() if len(matches) > 0: assert len(matches) == 1 # No duplicate matches. roi1 = backendDb.objectField(object1, 'roi') roi2 = backendDb.objectField(object2, 'roi') _drawMatch(img_stack, roi1, roi2, yoffset) matchesOf1[j1] = matches[0][0] matchesOf2[j2] = matches[0][0] # draw image scale = float(args.winsize) / max(img_stack.shape[0:2]) img_show = cv2.resize(img_stack, dsize=(0, 0), fx=scale, fy=scale) cv2.imshow('labelMatches', img_show[:, :, ::-1]) logging.info('Will draw %d matches found between the pair' % len(matchesOf1)) needRedraw = False # process mouse callback effect (button has been pressed) if mousePressed: i2 = None # reset after the last unsuccessful match logging.debug('Pressed x=%d, y=%d' % (xpress, ypress)) xpress /= scale ypress /= scale i1 = _findPressedObject(xpress, ypress, objects1) if i1 is not None: logging.debug('Found pressed object: %d' % i1) mousePressed = False # process mouse callback effect (button has been released) if mouseReleased: logging.debug('released x=%d, y=%d' % (xpress, ypress)) xpress /= scale ypress /= scale i2 = _findPressedObject(xpress, ypress - yoffset, objects2) if i2 is not None: logging.debug('Found released object: %d' % i2) mouseReleased = False # If we could find pressed and released objects, add match if i1 is not None and i2 is not None: # If one of the objects is already matched in this image pair, discard. if i1 in matchesOf1 or i2 in matchesOf2: logging.warning( 'One or two connected objects is already matched') i1 = i2 = None else: # Add the match to the list. objectid1 = backendDb.objectField(objects1[i1], 'objectid') objectid2 = backendDb.objectField(objects2[i2], 'objectid') logging.debug('i1 = %d, i2 = %d' % (i1, i2)) # Check if this object already in the matches. c.execute('SELECT match FROM matches WHERE objectid=?', (objectid1, )) matches1 = c.fetchall() c.execute('SELECT match FROM matches WHERE objectid=?', (objectid2, )) matches2 = c.fetchall() if len(matches1) > 1 or len(matches2) > 1: logging.error( 'One of the objectids %d, %d is in several matches.' % (objectid1, objectid2)) continue elif len(matches1) == 1 and len(matches2) == 1: logging.info( 'Will merge matches of objectids %d and %d' % (objectid1, objectid2)) c.execute('UPDATE matches SET match=? WHERE match=?', (matches1[0][0], matches2[0][0])) elif len(matches1) == 1 and len(matches2) == 0: logging.info('Add objectid %d to match %d' % (objectid2, matches1[0][0])) c.execute( 'INSERT INTO matches(match, objectid) VALUES (?,?)', (matches1[0][0], objectid2)) elif len(matches1) == 0 and len(matches2) == 1: logging.info('Add objectid %d to match %d' % (objectid1, matches2[0][0])) c.execute( 'INSERT INTO matches(match, objectid) VALUES (?,?)', (matches2[0][0], objectid1)) elif len(matches1) == 0 and len(matches2) == 0: logging.info( 'Add a new match between objectids %d and %d.' % (objectid1, objectid2)) # Find a free match index c.execute('SELECT MAX(match) FROM matches') maxmatch = c.fetchone()[0] match = int( maxmatch) + 1 if maxmatch is not None else 1 c.execute( 'INSERT INTO matches(match, objectid) VALUES (?,?)', (match, objectid1)) c.execute( 'INSERT INTO matches(match, objectid) VALUES (?,?)', (match, objectid2)) else: assert False # Reset when a new match is made. needRedraw = True i1 = i2 = None # Stay inside the loop inside one image pair until some button is pressed action = key_reader.parse(cv2.waitKey(50)) # Process pressed key (all except exit) if action == 'previous': logging.info('Previous image pair') if index_image == 1: logging.warning('already the first image pair') else: index_image -= 1 elif action == 'next': logging.info('Next image pair') index_image += 1 # exit at last image pair from outer loop elif action == 'delete_match': # if any car was selected, and it is matched if i1 is not None and i1 in matchesOf1: match = matchesOf1[i1] objectid1 = backendDb.objectField(objects1[i1], 'objectid') logging.info('deleting match %d' % match) c.execute( 'DELETE FROM matches WHERE match = ? AND objectid = ?', (match, objectid1)) else: logging.debug('delete is pressed, but no match is selected') cv2.destroyWindow("labelMatches")
def examineMatches(c, args): cv2.namedWindow("examineMatches") c.execute('SELECT DISTINCT(match) FROM matches') match_entries = c.fetchall() if args.shuffle: np.random.shuffle(match_entries) imreader = backendMedia.MediaReader(rootdir=args.rootdir) # For parsing keys. key_reader = KeyReader(args.key_dict) index_match = 0 # Iterating over images, because for each image we want to show all objects. while True: # Until a user hits the key for the "exit" action. (match, ) = match_entries[index_match] c.execute( 'SELECT * FROM objects WHERE objectid IN ' '(SELECT objectid FROM matches WHERE match=?)', (match, )) object_entries = c.fetchall() logging.info('Found %d objects for match %d' % (len(object_entries), match)) images = [] for object_entry in object_entries: imagefile = backendDb.objectField(object_entry, 'imagefile') objectid = backendDb.objectField(object_entry, 'objectid') roi = backendDb.objectField(object_entry, 'roi') score = backendDb.objectField(object_entry, 'score') image = imreader.imread(imagefile) util.drawScoredRoi(image, roi, score=score) scale = float(args.winsize) / max(image.shape[0:2]) image = cv2.resize(image, dsize=(0, 0), fx=scale, fy=scale) images.append(image) # Assume all images have the same size for now. # Assume there are not so many matches. image = np.hstack(images) # Display an image, wait for the key from user, and parse that key. cv2.imshow('examineMatches', image[:, :, ::-1]) action = key_reader.parse(cv2.waitKey(-1)) if action is None: # User pressed something which does not have an action. continue elif action == 'exit': break elif action == 'previous': index_match -= 1 elif action == 'next': index_match += 1 else: # User pressed something else which has an assigned action, # assume it is a new name. logging.info('Setting name "%s" to imagefile "%s"' % (action, imagefile)) c.execute('UPDATE images SET name=? WHERE imagefile=?' (action, imagefile)) index_match = index_match % len(match_entries) cv2.destroyWindow("examineMatches")
def examineObjects(c, args): cv2.namedWindow("examineObjects") c.execute('SELECT COUNT(*) FROM objects WHERE (%s) ' % args.where_object) logging.info('Found %d objects in db.' % c.fetchone()[0]) c.execute('SELECT DISTINCT imagefile FROM objects WHERE (%s) ' % args.where_object) image_entries = c.fetchall() logging.info('%d images found.' % len(image_entries)) if len(image_entries) == 0: logging.error('There are no images. Exiting.') return if args.shuffle: np.random.shuffle(image_entries) imreader = backendMedia.MediaReader(rootdir=args.rootdir) # For parsing keys. key_reader = KeyReader(args.key_dict) index_image = 0 index_object = 0 # Iterating over images, because for each image we want to show all objects. while True: # Until a user hits the key for the "exit" action. (imagefile, ) = image_entries[index_image] logging.info('Imagefile "%s"' % imagefile) image = imreader.imread(imagefile) scale = float(args.winsize) / max(image.shape[0:2]) image = cv2.resize(image, dsize=(0, 0), fx=scale, fy=scale) c.execute( 'SELECT * FROM objects WHERE imagefile=? AND (%s)' % args.where_object, (imagefile, )) object_entries = c.fetchall() logging.info('Found %d objects for image %s' % (len(object_entries), imagefile)) # Put the objects on top of the image. if len(object_entries) > 0: assert index_object < len(object_entries) object_entry = object_entries[index_object] objectid = backendDb.objectField(object_entry, 'objectid') roi = backendDb.objectField(object_entry, 'roi') score = backendDb.objectField(object_entry, 'score') name = backendDb.objectField(object_entry, 'name') scaledroi = [int(scale * r) for r in roi] # For displaying the scaled image. logging.info('objectid: %d, roi: %s, score: %s, name: %s' % (objectid, roi, score, name)) c.execute('SELECT * FROM polygons WHERE objectid=?', (objectid, )) polygon_entries = c.fetchall() if len(polygon_entries) > 0: logging.info('showing object with a polygon.') polygon = [(backendDb.polygonField(p, 'x'), backendDb.polygonField(p, 'y')) for p in polygon_entries] logging.debug('nonscaled polygon: %s' % pformat(polygon)) polygon = [(int(scale * x), int(scale * y)) for x, y in polygon] logging.debug('scaled polygon: %s' % pformat(polygon)) util.drawScoredPolygon(image, polygon, label=None, score=score) elif roi is not None: logging.info('showing object with a bounding box.') util.drawScoredRoi(image, scaledroi, label=None, score=score) else: raise Exception( 'Neither polygon, nor bbox is available for objectid %d' % objectid) c.execute('SELECT key,value FROM properties WHERE objectid=?', (objectid, )) properties = c.fetchall() if name is not None: properties.append(('name', name)) if score is not None: properties.append(('score', score)) for iproperty, (key, value) in enumerate(properties): cv2.putText(image, '%s: %s' % (key, value), (scaledroi[3] + 10, scaledroi[0] - 10 + util.SCALE * (iproperty + 1)), util.FONT, util.FONT_SIZE, (0, 0, 0), util.THICKNESS) cv2.putText(image, '%s: %s' % (key, value), (scaledroi[3] + 10, scaledroi[0] - 10 + util.SCALE * (iproperty + 1)), util.FONT, util.FONT_SIZE, (255, 255, 255), util.THICKNESS - 1) logging.info('objectid: %d. %s = %s.' % (objectid, key, value)) # Display an image, wait for the key from user, and parse that key. cv2.imshow('examineObjects', image[:, :, ::-1]) action = key_reader.parse(cv2.waitKey(-1)) if action is None: # User pressed something which does not have an action. continue elif action == 'exit': break elif action == 'previous': index_object -= 1 if index_object < 0: index_image -= 1 index_object = 0 elif action == 'next': index_object += 1 if index_object >= len(object_entries): index_image += 1 index_object = 0 elif action == 'delete' and len(object_entries) > 0: backendDb.deleteObject(c, objectid) del object_entries[index_object] if index_object >= len(object_entries): index_image += 1 index_object = 0 elif action == 'unname' and len(object_entries) > 0: logging.info('Remove the name from objectid "%s"' % objectid) c.execute('UPDATE objects SET name=NULL WHERE objectid=?', (objectid, )) index_image = index_image % len(image_entries) cv2.destroyWindow("examineObjects")
def labelObjects(c, args): cv2.namedWindow("labelObjects") c.execute('SELECT COUNT(*) FROM objects WHERE (%s) ' % args.where_object) logging.info('Found %d objects in db.' % c.fetchone()[0]) c.execute('SELECT * FROM objects WHERE (%s)' % args.where_object) object_entries = c.fetchall() logging.info('Found %d objects in db.' % len(object_entries)) if len(object_entries) == 0: return if args.shuffle: np.random.shuffle(object_entries) imreader = backendMedia.MediaReader(rootdir=args.rootdir) # For parsing keys. key_reader = KeyReader(args.key_dict) button = 0 index_object = 0 another_object = True while button != 27: go_next_object = False if another_object: another_object = False logging.info(' ') logging.info('Object %d out of %d' % (index_object, len(object_entries))) object_entry = object_entries[index_object] objectid = backendDb.objectField(object_entry, 'objectid') bbox = backendDb.objectField(object_entry, 'bbox') roi = backendDb.objectField(object_entry, 'roi') imagefile = backendDb.objectField(object_entry, 'imagefile') logging.info('imagefile: %s' % imagefile) image = imreader.imread(imagefile) # Display an image, wait for the key from user, and parse that key. scale = float(args.winsize) / max(image.shape[0:2]) logging.debug('Will resize image and annotations with scale: %f' % scale) image = cv2.resize(image, dsize=(0, 0), fx=scale, fy=scale) logging.info('objectid: %d, roi: %s' % (objectid, roi)) c.execute('SELECT * FROM polygons WHERE objectid=?', (objectid, )) polygon_entries = c.fetchall() if len(polygon_entries) > 0: logging.info('showing object with a polygon.') polygon = [(backendDb.polygonField(p, 'x'), backendDb.polygonField(p, 'y')) for p in polygon_entries] logging.debug('nonscaled polygon: %s' % pformat(polygon)) polygon = [(int(scale * p[0]), int(scale * p[1])) for p in polygon] logging.debug('scaled polygon: %s' % pformat(polygon)) util.drawScoredPolygon(image, polygon, label=None, score=None) elif roi is not None: logging.info('showing object with a bounding box.') logging.debug('nonscaled roi: %s' % pformat(roi)) roi = [int(scale * r) for r in roi] # For displaying the scaled image. logging.debug('scaled roi: %s' % pformat(roi)) util.drawScoredRoi(image, roi, label=None, score=None) else: raise Exception( 'Neither polygon, nor bbox is available for objectid %d' % objectid) c.execute( 'SELECT key,value FROM properties WHERE objectid=? AND key=?', (objectid, args.property)) # TODO: Multiple properties are possible because there is no # contraint on uniqueness on table properties(objectid,key). # Change when the uniqueness constraint is added to the # database schema. On the other hand, it's a feature. properties = c.fetchall() if len(properties) > 1: logging.warning( 'Multiple values for object %s and property %s. ' 'If reassigned, both will be changed' % (objectid, args.property)) for iproperty, (key, value) in enumerate(properties): cv2.putText(image, '%s: %s' % (key, value), (10, util.SCALE * (iproperty + 1)), util.FONT, util.FONT_SIZE, (0, 0, 0), util.THICKNESS) cv2.putText(image, '%s: %s' % (key, value), (10, util.SCALE * (iproperty + 1)), util.FONT, util.FONT_SIZE, (255, 255, 255), util.THICKNESS - 1) logging.info('objectid: %d. %s = %s.' % (objectid, key, value)) cv2.imshow('labelObjects', image[:, :, ::-1]) action = key_reader.parse(cv2.waitKey(-1)) if action == 'exit': break elif action == 'delete_label' and any_object_in_focus: logging.info('Remove label from objectid "%s"' % objectid) c.execute('DELETE FROM properties WHERE objectid=? AND key=?', (objectid, args.property)) go_next_object = True elif action is not None and action not in ['previous', 'next']: # User pressed something else which has an assigned action, # assume it is a new value. logging.info('Setting label "%s" to objectid "%s"' % (action, objectid)) if len(properties) > 0: c.execute('DELETE FROM properties WHERE objectid=? AND key=?', (objectid, args.property)) c.execute( 'INSERT INTO properties(objectid,key,value) VALUES (?,?,?)', (objectid, args.property, str(action))) go_next_object = True # Navigation. if action == 'previous': logging.debug('previous object') another_object = True if index_object > 0: index_object -= 1 else: logging.warning('Already at the first object.') elif action == 'next' or go_next_object == True: logging.debug('next object') another_object = True if index_object < len(object_entries) - 1: index_object += 1 else: logging.warning( 'Already at the last object. Press Esc to save and exit.') cv2.destroyWindow("labelObjects")
def examineImages(c, args): cv2.namedWindow("examineImages") c.execute('SELECT * FROM images WHERE (%s)' % args.where_image) image_entries = c.fetchall() logging.info('%d images found.' % len(image_entries)) if len(image_entries) == 0: logging.error('There are no images. Exiting.') return if args.shuffle: np.random.shuffle(image_entries) if args.snapshot_dir and not op.exists(args.snapshot_dir): os.makedirs(args.snapshot_dir) imreader = backendMedia.MediaReader(rootdir=args.rootdir) # For parsing keys. key_reader = KeyReader(args.key_dict) # For overlaying masks. labelmap = literal_eval( args.mask_mapping_dict) if args.mask_mapping_dict else None logging.info('Parsed mask_mapping_dict to %s' % pformat(labelmap)) index_image = 0 while True: # Until a user hits the key for the "exit" action. image_entry = image_entries[index_image] imagefile = backendDb.imageField(image_entry, 'imagefile') maskfile = backendDb.imageField(image_entry, 'maskfile') imname = backendDb.imageField(image_entry, 'name') imscore = backendDb.imageField(image_entry, 'score') logging.info('Imagefile "%s"' % imagefile) logging.debug('Image name="%s", score=%s' % (imname, imscore)) image = imreader.imread(imagefile) # Overlay the mask. if maskfile is not None: mask = imreader.maskread(maskfile) if args.mask_aside: image = util.drawMaskAside(image, mask, labelmap=labelmap) elif args.mask_alpha is not None: image = util.drawMaskOnImage(image, mask, alpha=args.mask_alpha, labelmap=labelmap) else: logging.info('No mask for this image.') # Put the objects on top of the image. if args.with_objects: c.execute('SELECT * FROM objects WHERE imagefile=?', (imagefile, )) object_entries = c.fetchall() logging.info('Found %d objects for image %s' % (len(object_entries), imagefile)) for object_entry in object_entries: objectid = backendDb.objectField(object_entry, 'objectid') roi = backendDb.objectField(object_entry, 'roi') score = backendDb.objectField(object_entry, 'score') name = backendDb.objectField(object_entry, 'name') logging.info('objectid: %d, roi: %s, score: %s, name: %s' % (objectid, roi, score, name)) c.execute('SELECT * FROM polygons WHERE objectid=?', (objectid, )) polygon_entries = c.fetchall() if len(polygon_entries) > 0: logging.info('showing object with a polygon.') polygon = [(int(backendDb.polygonField(p, 'x')), int(backendDb.polygonField(p, 'y'))) for p in polygon_entries] util.drawScoredPolygon(image, polygon, label=name, score=score) elif roi is not None: logging.info('showing object with a bounding box.') util.drawScoredRoi(image, roi, label=name, score=score) else: logging.warning( 'Neither polygon, nor bbox is available for objectid %d' % objectid) # Display an image, wait for the key from user, and parse that key. scale = float(args.winsize) / max(list(image.shape[0:2])) scaled_image = cv2.resize(image, dsize=(0, 0), fx=scale, fy=scale) # Overlay imagefile. if args.with_imagefile: util.drawTextOnImage( scaled_image, op.basename(backendMedia.normalizeSeparators(imagefile))) # Overlay score. # TODO: add y offset, if necessary if args.with_score: util.drawTextOnImage(scaled_image, '%.3f' % imscore) # Display cv2.imshow('examineImages', scaled_image[:, :, ::-1]) action = key_reader.parse(cv2.waitKey(-1)) if action is None: # User pressed something which does not have an action. continue elif action == 'snapshot': if args.snapshot_dir: snaphot_path = op.join(args.snapshot_dir, '%08d.png' % index_image) logging.info('Making a snapshot at path: %s' % snaphot_path) imageio.imwrite(snaphot_path, image) else: logging.warning( 'The user pressed a snapshot key, but snapshot_dir is not ' 'specified. Will not write a snapshot.') elif action == 'delete': backendDb.deleteImage(c, imagefile) del image_entries[index_image] if len(image_entries) == 0: logging.warning('Deleted the last image.') break index_image += 1 elif action == 'exit': break elif action == 'previous': index_image -= 1 elif action == 'next': index_image += 1 else: # User pressed something else which has an assigned action, assume it is a new name. logging.info('Setting name "%s" to imagefile "%s"' % (action, imagefile)) c.execute('UPDATE images SET name=? WHERE imagefile=?' (action, imagefile)) index_image = index_image % len(image_entries) cv2.destroyWindow("examineImages")
def importKitti(c, args): if args.with_display: imreader = backendMedia.MediaReader(args.rootdir) image_paths = sorted(glob(op.join(args.images_dir, '*.png'))) logging.info('Found %d PNG images in %s' % (len(image_paths), args.images_dir)) for image_path in progressbar(image_paths): filename = op.splitext(op.basename(image_path))[0] logging.debug('Processing image: "%s"' % filename) # Add image to the database. imheight, imwidth = backendMedia.getPictureSize(image_path) imagefile = op.relpath(image_path, args.rootdir) c.execute('INSERT INTO images(imagefile,width,height) VALUES (?,?,?)', (imagefile, imwidth, imheight)) if args.with_display: img = imreader.imread(imagefile) # Detection annotations. if args.detection_dir: detection_path = op.join(args.detection_dir, '%s.txt' % filename) if not op.exists(detection_path): raise FileNotFoundError('Annotation file not found at "%s".' % detection_path) # Read annotation file. with open(detection_path) as f: lines = f.read().splitlines() logging.debug('Read %d lines from detection file "%s"' % (len(lines), detection_path)) for line in lines: objectid = _parseObject(c, line, imagefile) if args.with_display: c.execute('SELECT * FROM objects WHERE objectid=?', (objectid, )) object_entry = c.fetchone() name = backendDb.objectField(object_entry, 'name') roi = backendDb.objectField(object_entry, 'roi') score = backendDb.objectField(object_entry, 'score') util.drawScoredRoi(img, roi, name, score=score) # Segmentation annotations. if args.segmentation_dir: segmentation_path = op.join(args.segmentation_dir, '%s.png' % filename) if not op.exists(segmentation_path): raise FileNotFoundError('Annotation file not found at "%s".' % segmentation_path) # Add image to the database. maskfile = op.relpath(segmentation_path, args.rootdir) c.execute('UPDATE images SET maskfile=? WHERE imagefile=?', (maskfile, imagefile)) if args.with_display: mask = imreader.maskread(maskfile) img = util.drawMaskAside(img, mask, labelmap=None) # Maybe display. if args.with_display: cv2.imshow('importKitti', img[:, :, ::-1]) if cv2.waitKey(-1) == 27: args.with_display = False cv2.destroyWindow('importKitti')
def filterObjectsByIntersection(c, args): def getRoiIntesection(rioi1, roi2): dy = min(roi1[2], roi2[2]) - max(roi1[0], roi2[0]) dx = min(roi1[3], roi2[3]) - max(roi1[1], roi2[1]) if dy <= 0 or dx <= 0: return 0 return dy * dx if args.with_display: imreader = backendMedia.MediaReader(rootdir=args.rootdir) c.execute('SELECT imagefile FROM images') for imagefile, in progressbar(c.fetchall()): if args.with_display: image = imreader.imread(imagefile) c.execute('SELECT * FROM objects WHERE imagefile=?', (imagefile, )) object_entries = c.fetchall() logging.debug('%d objects found for %s' % (len(object_entries), imagefile)) good_objects = np.ones(shape=len(object_entries), dtype=bool) for iobject1, object_entry1 in enumerate(object_entries): #roi1 = _expandCarBbox_(object_entry1, args) roi1 = backendDb.objectField(object_entry1, 'roi') if roi1 is None: logging.error( 'No roi for objectid %d, intersection on polygons ' 'not implemented.', iobject1) continue area1 = (roi1[2] - roi1[0]) * (roi1[3] - roi1[1]) if area1 == 0: logging.warning('An object in %s has area 0. Will delete.' % imagefile) good_objects[iobject1] = False break for iobject2, object_entry2 in enumerate(object_entries): if iobject2 == iobject1: continue roi2 = backendDb.objectField(object_entry2, 'roi') if roi2 is None: continue intersection = getRoiIntesection(roi1, roi2) / float(area1) if intersection > args.intersection_thresh: good_objects[iobject1] = False break if args.with_display: image = imreader.imread(imagefile) util.drawScoredRoi(image, roi1, score=(1 if good_objects[iobject1] else 0)) for iobject2, object_entry2 in enumerate(object_entries): if iobject1 == iobject2: continue roi2 = backendDb.objectField(object_entry2, 'roi') if roi2 is None: continue util.drawScoredRoi(image, roi2, score=0.5) cv2.imshow('filterObjectsByIntersection', image[:, :, ::-1]) key = cv2.waitKey(-1) if key == 27: cv2.destroyWindow('filterObjectsByIntersection') args.with_display = 0 for object_entry, is_object_good in zip(object_entries, good_objects): if not is_object_good: backendDb.deleteObject( c, backendDb.objectField(object_entry, 'objectid'))
def filterObjectsAtBorder(c, args): def isPolygonAtBorder(polygon_entries, width, height, border_thresh_perc): xs = [backendDb.polygonField(p, 'x') for p in polygon_entries] ys = [backendDb.polygonField(p, 'y') for p in polygon_entries] border_thresh = (height + width) / 2 * border_thresh_perc dist_to_border = min(xs, [width - x for x in xs], ys, [height - y for y in ys]) num_too_close = sum([x < border_thresh for x in dist_to_border]) return num_too_close >= 2 def isRoiAtBorder(roi, width, height, border_thresh_perc): border_thresh = (height + width) / 2 * border_thresh_perc logging.debug('border_thresh: %f' % border_thresh) return min(roi[0], roi[1], height + 1 - roi[2], width + 1 - roi[3]) < border_thresh if args.with_display: imreader = backendMedia.MediaReader(rootdir=args.rootdir) # For the reference. c.execute('SELECT COUNT(1) FROM objects') num_before = c.fetchone()[0] c.execute('SELECT imagefile FROM images') for imagefile, in progressbar(c.fetchall()): if args.with_display: image = imreader.imread(imagefile) c.execute('SELECT width,height FROM images WHERE imagefile=?', (imagefile, )) (imwidth, imheight) = c.fetchone() c.execute('SELECT * FROM objects WHERE imagefile=?', (imagefile, )) object_entries = c.fetchall() logging.debug('%d objects found for %s' % (len(object_entries), imagefile)) for object_entry in object_entries: for_deletion = False # Get all necessary entries. objectid = backendDb.objectField(object_entry, 'objectid') roi = backendDb.objectField(object_entry, 'roi') c.execute('SELECT * FROM polygons WHERE objectid=?', (objectid, )) polygon_entries = c.fetchall() # Find if the polygon or the roi is at the border, # polygon has preference over roi. if len(polygon_entries) > 0: if isPolygonAtBorder(polygon_entries, imwidth, imheight, args.border_thresh): logging.debug('border polygon %s' % str(polygon)) for_deletion = True elif roi is not None: if isRoiAtBorder(roi, imwidth, imheight, args.border_thresh): logging.debug('border roi %s' % str(roi)) for_deletion = True else: logging.error( 'Neither polygon, nor bbox is available for objectid %d' % objectid) # Draw polygon or roi. if args.with_display: if len(polygon_entries) > 0: polygon = [(backendDb.polygonField(p, 'x'), backendDb.polygonField(p, 'y')) for p in polygon_entries] util.drawScoredPolygon(image, polygon, score=(0 if for_deletion else 1)) elif roi is not None: util.drawScoredRoi(image, roi, score=(0 if for_deletion else 1)) # Delete if necessary if for_deletion: backendDb.deleteObject(c, objectid) if args.with_display: cv2.imshow('filterObjectsAtBorder', image[:, :, ::-1]) key = cv2.waitKey(-1) if key == 27: args.with_display = False cv2.destroyWindow('filterObjectsAtBorder') # For the reference. c.execute('SELECT COUNT(1) FROM objects') num_after = c.fetchone()[0] logging.info('Deleted %d out of %d objects.' % (num_before - num_after, num_before))
def importBdd(c, args): if args.with_display: imreader = backendMedia.MediaReader(args.rootdir) image_paths = sorted(glob(op.join(args.images_dir, '*.jpg'))) logging.info('Found %d JPG images in %s' % (len(image_paths), args.images_dir)) if args.detection_json: if not op.exists(args.detection_json): raise FileNotFoundError('Annotation file not found at "%s".' % args.detection_json) logging.info( 'Loading the json with annotations. This may take a few seconds.') with open(args.detection_json) as f: detections = json.load(f) # Dict with image name as the key. detections = {d['name']: d for d in detections} for image_path in progressbar(image_paths): filename = op.splitext(op.basename(image_path))[0] logging.debug('Processing image: "%s"' % filename) # Add image to the database. imheight, imwidth = backendMedia.getPictureSize(image_path) imagefile = op.relpath(image_path, args.rootdir) c.execute('INSERT INTO images(imagefile,width,height) VALUES (?,?,?)', (imagefile, imwidth, imheight)) if args.with_display: img = imreader.imread(imagefile) # Detection annotations. if args.detection_json: imagename = op.basename(imagefile) if imagename not in detections: logging.error('Cant find image name "%s" in "%s"', args.detection_json, imagename) continue detections_for_image = detections[imagename] image_properties = detections_for_image['attributes'] for object_ in detections_for_image['labels']: object_bddid = object_['id'] object_name = object_['category'] object_properties = { key: value for key, value in object_['attributes'].items() if value != 'none' } object_properties.update(image_properties) # Skip 3d object. TODO: import it to properties. if 'box3d' in object_: logging.warning('Will skip 3D object %d.' % object_bddid) continue # Get the bbox if exists. x1 = y1 = width = height = None if 'box2d' in object_: box2d = object_['box2d'] x1 = int(float(box2d['x1'])) y1 = int(float(box2d['y1'])) width = int(float(box2d['x2']) - x1) height = int(float(box2d['y2']) - y1) if args.with_display: roi = utilBoxes.bbox2roi((x1, y1, width, height)) util.drawScoredRoi(img, roi, object_name) c.execute( 'INSERT INTO objects(imagefile,x1,y1,width,height,name) ' 'VALUES (?,?,?,?,?,?)', (imagefile, x1, y1, width, height, object_name)) objectid = c.lastrowid # Get the polygon if it exists. if 'poly2d' in object_: if len(object_['poly2d']) > 1: assert 0, len(object_['poly2d']) polygon = object_['poly2d'][0] polygon_name = None if polygon['closed'] else 'open_loop' for pt in polygon['vertices']: c.execute( 'INSERT INTO polygons(objectid,x,y,name) ' 'VALUES (?,?,?,?)', (objectid, pt[0], pt[1], polygon_name)) if args.with_display: util.drawScoredPolygon(img, [(int(x[0]), int(x[1])) for x in polygon['vertices']], object_name) # Insert image-level and object-level attributes into # "properties" table. for key, value in object_properties.items(): c.execute( 'INSERT INTO properties(objectid,key,value) VALUES (?,?,?)', (objectid, key, value)) # Segmentation annotations. if args.segmentation_dir: segmentation_path = op.join(args.segmentation_dir, '%s_train_id.png' % filename) if not op.exists(segmentation_path): raise FileNotFoundError('Annotation file not found at "%s".' % segmentation_path) # Add image to the database. maskfile = op.relpath(segmentation_path, args.rootdir) c.execute('UPDATE images SET maskfile=? WHERE imagefile=?', (maskfile, imagefile)) if args.with_display: mask = imreader.maskread(maskfile) img = util.drawMaskAside(img, mask, labelmap=None) # Maybe display. if args.with_display: cv2.imshow('importKitti', img[:, :, ::-1]) if cv2.waitKey(-1) == 27: args.with_display = False cv2.destroyWindow('importKitti')