Skip to content

Commit

Permalink
Added class colors to run_tf_detector
Browse files Browse the repository at this point in the history
  • Loading branch information
agentmorris committed May 17, 2019
1 parent 503215e commit 0ec6b88
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 23 deletions.
4 changes: 4 additions & 0 deletions api/detector_batch_processing/find_problematic_detections.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class SuspiciousDetectionResults:

masterHtmlFile = None

filterFile = None


class IndexedDetection:
'''
Expand Down Expand Up @@ -852,6 +854,8 @@ def find_suspicious_detections(inputCsvFilename,outputCsvFilename,options=None):
with open(detectionIndexFileName, 'w') as f:
f.write(s)

toReturn.filterFile = detectionIndexFileName

return toReturn

# ...find_suspicious_detections()
Expand Down
17 changes: 10 additions & 7 deletions api/detector_batch_processing/postprocess_batch_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,12 @@ def render_bounding_boxes(image_base_dir,image_relative_path,


def prepare_html_subpages(images_html,output_dir,options=None):
"""
Write out a series of html image lists, e.g. the fp/tp/fn/tn pages.
image_html is a dictionary mapping an html page name (e.g. "fp") to a list
of image structs friendly to write_html_image_list
"""
if options is None:
options = PostProcessingOptions()

Expand Down Expand Up @@ -499,11 +504,9 @@ def process_batch_results(options):
else:
res = 'tn'

display_name = '<b>Result type</b>: {}, <b>Presence</b>: {}, <b>Class</b>: {}, <b>Image</b>: {}'.format(
res.upper(),
str(gt_presence),
gt_class_name,
image_relative_path)
display_name = '<b>Result type</b>: {}, <b>Presence</b>: {}, <b>Class</b>: {}, <b>Max conf</b>: {:0.2f}%, <b>Image</b>: {}'.format(
res.upper(), str(gt_presence), gt_class_name,
max_conf * 100, image_relative_path)

rendered_image_html_info = render_bounding_boxes(options.image_base_dir,
image_relative_path,
Expand All @@ -530,8 +533,8 @@ def process_batch_results(options):
<a href="tn.html">True negatives (tn)</a> ({})<br/>
<a href="fp.html">False positives (fp)</a> ({})<br/>
<a href="fn.html">False negatives (fn)</a> ({})<br/>
<br/><p>At a confidence threshold of {:0.1f}%, precision={:0.2f}, recall={:0.2f}</p>
<br/><p><strong>Precision/recall summary for all {} images</strong></p><img src="{}"><br/>
<p>At a confidence threshold of {:0.1f}%, precision={:0.2f}, recall={:0.2f}</p>
<p><strong>Precision/recall summary for all {} images</strong></p><img src="{}"><br/>
</body></html>""".format(
count, confidence_threshold * 100,
image_counts['tp'], image_counts['tn'], image_counts['fp'], image_counts['fn'],
Expand Down
2 changes: 1 addition & 1 deletion detection/detection_eval/load_api_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def load_api_results(filename,normalize_paths=True,filename_replacements={}):
fn = fn.replace(string_to_replace,replacement_string)
detection_results.at[iRow,'image_path'] = fn

print('Finished loading and de-serializing API results from {}'.format(filename))
print('Finished loading and de-serializing API results for {} images from {}'.format(len(detection_results),filename))

return detection_results

Expand Down
63 changes: 48 additions & 15 deletions detection/run_tf_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
#
# See the "test driver" cell for example invocation.
#
# It's clearly icky that if you give it blah.jpg, it writes the results to
# blah.detections.jpg, but I'm defending this with the "just a demo script"
# argument.
# If no output directory is specified, writes detections for c:\foo\bar.jpg to
# c:\foo\bar_detections.jpg .
#
######

Expand All @@ -37,6 +36,9 @@
# Stick this into filenames before the extension for the rendered result
DETECTION_FILENAME_INSERT = '_detections'

BOX_COLORS = ['r','g','b']
DEFAULT_LINE_WIDTH = 10


#%% Core detection functions

Expand Down Expand Up @@ -197,7 +199,7 @@ def generate_detections(detection_graph,images):
#%% Rendering functions

def render_bounding_box(box, score, classLabel, inputFileName, outputFileName=None,
confidenceThreshold=DEFAULT_CONFIDENCE_THRESHOLD,linewidth=2):
confidenceThreshold=DEFAULT_CONFIDENCE_THRESHOLD,linewidth=DEFAULT_LINE_WIDTH):
"""
Convenience wrapper to apply render_bounding_boxes to a single image
"""
Expand All @@ -209,6 +211,7 @@ def render_bounding_box(box, score, classLabel, inputFileName, outputFileName=No
render_bounding_boxes(boxes,scores,[classLabel],[inputFileName],outputFileNames,
confidenceThreshold,linewidth)


def render_bounding_boxes(boxes, scores, classes, inputFileNames, outputFileNames=[],
confidenceThreshold=DEFAULT_CONFIDENCE_THRESHOLD,linewidth=2):
"""
Expand Down Expand Up @@ -276,7 +279,8 @@ def render_bounding_boxes(boxes, scores, classes, inputFileNames, outputFileName
# Origin is the upper-left
iLeft = x
iBottom = y
rect = patches.Rectangle((iLeft,iBottom),w,h,linewidth=linewidth,edgecolor='r',
boxColor = BOX_COLORS[int(classes[iImage][iBox]) % len(BOX_COLORS)]
rect = patches.Rectangle((iLeft,iBottom),w,h,linewidth=linewidth,edgecolor=boxColor,
facecolor='none')

# Add the patch to the Axes
Expand Down Expand Up @@ -304,7 +308,7 @@ def render_bounding_boxes(boxes, scores, classes, inputFileNames, outputFileName
# ...def render_bounding_boxes


def load_and_run_detector(modelFile, imageFileNames,
def load_and_run_detector(modelFile, imageFileNames, outputDir=None,
confidenceThreshold=DEFAULT_CONFIDENCE_THRESHOLD, detection_graph=None):

if len(imageFileNames) == 0:
Expand All @@ -323,12 +327,27 @@ def load_and_run_detector(modelFile, imageFileNames,

assert len(boxes) == len(imageFileNames)

outputFileNames = []
outputFullPaths = []
outputFileNames = {}
if outputDir is not None:
os.makedirs(outputDir,exist_ok=True)
for iFn,fullInputPath in enumerate(imageFileNames):
fn = os.path.basename(fullInputPath).lower()

# Since we'll be writing a bunch of files to the same folder, rename
# as necessary to avoid collisions
if fn in outputFileNames:
nCollisions = outputFileNames[fn]
fn = str(nCollisions) + '_' + fn
outputFileNames[fn] = nCollisions + 1
else:
outputFileNames[fn] = 0
outputFullPaths.append(os.path.join(outputDir,fn))

plt.ioff()

render_bounding_boxes(boxes=boxes, scores=scores, classes=classes,
inputFileNames=imageFileNames, outputFileNames=outputFileNames,
inputFileNames=imageFileNames, outputFileNames=outputFullPaths,
confidenceThreshold=confidenceThreshold)

return detection_graph
Expand All @@ -353,7 +372,8 @@ def load_and_run_detector(modelFile, imageFileNames,
imageFileNames = [r"D:\temp\test\1\NE2881-9_RCNX0195_xparent.png"]

detection_graph = load_and_run_detector(modelFile,imageFileNames,
DEFAULT_CONFIDENCE_THRESHOLD,detection_graph)
confidenceThreshold=DEFAULT_CONFIDENCE_THRESHOLD,
detection_graph=detection_graph)


#%% File helper functions
Expand Down Expand Up @@ -406,10 +426,19 @@ def main():

parser = argparse.ArgumentParser()
parser.add_argument('detectorFile', type=str)
parser.add_argument('--imageDir', action='store', type=str, default='', help='Directory to search for images, with optional recursion')
parser.add_argument('--imageFile', action='store', type=str, default='', help='Single file to process, mutually exclusive with imageDir')
parser.add_argument('--threshold', action='store', type=float, default=DEFAULT_CONFIDENCE_THRESHOLD, help='Confidence threshold, don''t render boxes below this confidence')
parser.add_argument('--recursive', action='store_true', help='Recurse into directories, only meaningful if using --imageDir')
parser.add_argument('--imageDir', action='store', type=str, default='',
help='Directory to search for images, with optional recursion')
parser.add_argument('--imageFile', action='store', type=str, default='',
help='Single file to process, mutually exclusive with imageDir')
parser.add_argument('--threshold', action='store', type=float,
default=DEFAULT_CONFIDENCE_THRESHOLD,
help='Confidence threshold, don''t render boxes below this confidence')
parser.add_argument('--recursive', action='store_true',
help='Recurse into directories, only meaningful if using --imageDir')
parser.add_argument('--forceCpu', action='store_true',
help='Force CPU detection, even if a GPU is available')
parser.add_argument('--outputDir', type=str, default=None,
help='Directory for output images (defaults to same as input)')

if len(sys.argv[1:])==0:
parser.print_help()
Expand All @@ -426,14 +455,18 @@ def main():
imageFileNames = [args.imageFile]
else:
imageFileNames = findImages(args.imageDir,args.recursive)


if args.forceCpu:
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'

# Hack to avoid running on already-detected images
imageFileNames = [x for x in imageFileNames if DETECTION_FILENAME_INSERT not in x]

print('Running detector on {} images'.format(len(imageFileNames)))

load_and_run_detector(modelFile=args.detectorFile, imageFileNames=imageFileNames,
confidenceThreshold=args.threshold)
confidenceThreshold=args.threshold, outputDir=args.outputDir)


if __name__ == '__main__':
Expand Down

0 comments on commit 0ec6b88

Please sign in to comment.