def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict(prog=prog, version=version,) args = docopt(__doc__ % completions, argv=arguments, version='Signal extractor for videos (%s)' % version,) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) hrdir = get_parameter(args, configuration, 'hrdir', 'hr') resultdir = get_parameter(args, configuration, 'resultdir', 'results') overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, args['--verbose']) # TODO: find a way to check protocol names - Guillaume HEUSCH, 22-06-2018 if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # errors errors = [] rmse = 0; mean_error_percentage = 0 inferred = [] ground_truth = [] ################ ### LET'S GO ### ################ # if output dir exists and not overwriting, stop if os.path.exists(resultdir) and not overwrite: logger.info("Skipping output `%s': already exists, use --overwrite to force an overwrite", resultdir) sys.exit() else: bob.io.base.create_directories_safe(resultdir) for obj in objects: # load the heart rate logger.debug("Loading computed heart rate from `%s'...", obj.path) hr_file = obj.make_path(hrdir, '.hdf5') try: hr = bob.io.base.load(hr_file) except (IOError, RuntimeError) as e: logger.warn("Skipping file `%s' (no heart rate file available)", obj.path) continue hr = hr[0] logger.debug("Computed heart rate : {0}".format(hr)) # load ground truth gt = obj.load_heart_rate_in_bpm() logger.debug("Real heart rate : {0}".format(gt)) ground_truth.append(gt) inferred.append(hr) error = hr - gt logger.debug("Error = {0}".format(error)) errors.append(error) rmse += error**2 mean_error_percentage += numpy.abs(error)/gt # compute global statistics rmse /= len(errors) rmse = numpy.sqrt(rmse) rmse_text = "Root Mean Squared Error = {0:.2f}". format(rmse) mean_error_percentage /= len(errors) mean_err_percent_text = "Mean of error-rate percentage = {0:.2f}". format(mean_error_percentage) from scipy.stats import pearsonr correlation, p = pearsonr(inferred, ground_truth) pearson_text = "Pearson's correlation = {0:.2f} ({1:.2f} significance)". format(correlation, p) logger.info("==================") logger.info("=== STATISTICS ===") logger.info(rmse_text) logger.info(mean_err_percent_text) logger.info(pearson_text) # statistics in a text file stats_filename = os.path.join(resultdir, 'stats.txt') stats_file = open(stats_filename, 'w') stats_file.write(rmse_text + "\n") stats_file.write(mean_err_percent_text + "\n") stats_file.write(pearson_text + "\n") stats_file.close() # scatter plot from matplotlib import pyplot f = pyplot.figure() ax = f.add_subplot(1,1,1) ax.scatter(ground_truth, inferred) ax.plot([40, 110], [40, 110], 'r--', lw=2) pyplot.xlabel('Ground truth [bpm]') pyplot.ylabel('Estimated heart-rate [bpm]') ax.set_title('Scatter plot') scatter_file = os.path.join(resultdir, 'scatter.png') pyplot.savefig(scatter_file) # histogram of error f2 = pyplot.figure() ax2 = f2.add_subplot(1,1,1) ax2.hist(errors, bins=50, ) ax2.set_title('Distribution of the error') distribution_file = os.path.join(resultdir, 'error_distribution.png') pyplot.savefig(distribution_file) # distribution of HR f3 = pyplot.figure() ax3 = f3.add_subplot(1,1,1) histoargs = {'bins': 50, 'alpha': 0.5, 'histtype': 'bar', 'range': (30, 120)} pyplot.hist(ground_truth, label='Real HR', color='g', **histoargs) pyplot.hist(inferred, label='Estimated HR', color='b', **histoargs) pyplot.ylabel("Test set") if plot: pyplot.show() return 0
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Signal extractor for videos (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) pulsedir = get_parameter(args, configuration, 'pulsedir', 'pulse') start = get_parameter(args, configuration, 'start', 0) end = get_parameter(args, configuration, 'end', 0) motion = get_parameter(args, configuration, 'motion', 0.0) threshold = get_parameter(args, configuration, 'threshold', 0.5) skininit = get_parameter(args, configuration, 'skininit', False) framerate = get_parameter(args, configuration, 'framerate', 61) order = get_parameter(args, configuration, 'order', 128) window = get_parameter(args, configuration, 'window', 0) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) gridcount = get_parameter(args, configuration, 'gridcount', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, verbosity_level) if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # if we are on a grid environment, just find what I have to process. sge = False try: sge = os.environ.has_key('SGE_TASK_ID') # python2 except AttributeError: sge = 'SGE_TASK_ID' in os.environ # python3 if sge: pos = int(os.environ['SGE_TASK_ID']) - 1 if pos >= len(objects): raise RuntimeError( "Grid request for job {} on a setup with {} jobs".format( pos, len(objects))) objects = [objects[pos]] if gridcount: print(len(objects)) sys.exit() # build the bandpass filter one and for all bandpass_filter = build_bandpass_filter(framerate, order, plot) # does the actual work - for every video in the available dataset, # extract the signals and dumps the results to the corresponding directory for obj in objects: # expected output file output = obj.make_path(pulsedir, '.hdf5') # if output exists and not overwriting, skip this file if os.path.exists(output) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load video video = obj.load_video(configuration.dbdir) logger.info("Processing input video from `%s'...", video.filename) # indices where to start and to end the processing logger.debug("Sequence length = {0}".format(len(video))) start_index = start end_index = end if (end_index == 0): end_index = len(video) if end_index > len(video): logger.warn("Skipping Sequence {0} : not long enough ({1})".format( obj.path, len(video))) continue # number of final frames nb_frames = len(video) if end_index > 0: nb_frames = end_index - start_index # the grayscale difference between two consecutive frames (for stable frame selection) if motion: diff_motion = numpy.zeros((nb_frames - 1, 1), dtype='float64') # load the result of face detection bounding_boxes = obj.load_face_detection() # skin color filter skin_filter = bob.ip.skincolorfilter.SkinColorFilter() # output data output_data = numpy.zeros(nb_frames, dtype='float64') chrom = numpy.zeros((nb_frames, 2), dtype='float64') # loop on video frames counter = 0 for i, frame in enumerate(video): if i >= start_index and i < end_index: logger.debug("Processing frame %d/%d...", i + 1, end_index) try: bbox = bounding_boxes[i] except NameError: bbox, quality = bob.ip.facedetect.detect_single_face(frame) # motion difference (if asked for) if motion > 0 and (i < (len(video) - 1)) and (counter > 0): current = crop_face(frame, bbox, bbox.size[1]) diff_motion[counter - 1] = compute_gray_diff(face, current) face = crop_face(frame, bbox, bbox.size[1]) if plot and verbosity_level >= 2: from matplotlib import pyplot pyplot.imshow(numpy.rollaxis(numpy.rollaxis(face, 2), 2)) pyplot.show() # skin filter if counter == 0 or skininit: skin_filter.estimate_gaussian_parameters(face) logger.debug( "Skin color parameters:\nmean\n{0}\ncovariance\n{1}". format(skin_filter.mean, skin_filter.covariance)) skin_mask = skin_filter.get_skin_mask(face, threshold) if plot and verbosity_level >= 2: from matplotlib import pyplot skin_mask_image = numpy.copy(face) skin_mask_image[:, skin_mask] = 255 pyplot.imshow( numpy.rollaxis(numpy.rollaxis(skin_mask_image, 2), 2)) pyplot.show() # sometimes skin is not detected ! if numpy.count_nonzero(skin_mask) != 0: # compute the mean rgb values of the skin pixels r, g, b = compute_mean_rgb(face, skin_mask) logger.debug( "Mean color -> R = {0}, G = {1}, B = {2}".format( r, g, b)) # project onto the chrominance colorspace chrom[counter] = project_chrominance(r, g, b) logger.debug("Chrominance -> X = {0}, Y = {1}".format( chrom[counter][0], chrom[counter][1])) else: logger.warn( "No skin pixels detected in frame {0}, using previous value" .format(i)) # very unlikely, but it could happened and messed up all experiments (averaging of scores ...) if counter == 0: chrom[counter] = project_chrominance(128., 128., 128.) else: chrom[counter] = chrom[counter - 1] counter += 1 elif i > end_index: break # select the most stable number of consecutive frames, if asked for if motion > 0: n_stable_frames_to_keep = int(motion * nb_frames) logger.info( "Number of stable frames kept for motion -> {0}".format( n_stable_frames_to_keep)) index = select_stable_frames(diff_motion, n_stable_frames_to_keep) logger.info("Stable segment -> {0} - {1}".format( index, index + n_stable_frames_to_keep)) chrom = chrom[index:(index + n_stable_frames_to_keep), :] if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(2, sharex=True) axarr[0].plot(range(chrom.shape[0]), chrom[:, 0], 'k') axarr[0].set_title("X value in the chrominance subspace") axarr[1].plot(range(chrom.shape[0]), chrom[:, 1], 'k') axarr[1].set_title("Y value in the chrominance subspace") pyplot.show() # now that we have the chrominance signals, apply bandpass from scipy.signal import filtfilt x_bandpassed = numpy.zeros(nb_frames, dtype='float64') y_bandpassed = numpy.zeros(nb_frames, dtype='float64') x_bandpassed = filtfilt(bandpass_filter, numpy.array([1]), chrom[:, 0]) y_bandpassed = filtfilt(bandpass_filter, numpy.array([1]), chrom[:, 1]) if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(2, sharex=True) axarr[0].plot(range(x_bandpassed.shape[0]), x_bandpassed, 'k') axarr[0].set_title("X bandpassed") axarr[1].plot(range(y_bandpassed.shape[0]), y_bandpassed, 'k') axarr[1].set_title("Y bandpassed") pyplot.show() # build the final pulse signal alpha = numpy.std(x_bandpassed) / numpy.std(y_bandpassed) pulse = x_bandpassed - alpha * y_bandpassed # overlap-add if window_size != 0 if window > 0: window_size = window window_stride = window_size / 2 for w in range(0, (len(pulse) - window_size), window_stride): pulse[w:w + window_size] = 0.0 xw = x_bandpassed[w:w + window_size] yw = y_bandpassed[w:w + window_size] alpha = numpy.std(xw) / numpy.std(yw) sw = xw - alpha * yw sw *= numpy.hanning(window_size) pulse[w:w + window_size] += sw if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(1) pyplot.plot(range(pulse.shape[0]), pulse, 'k') pyplot.title("Pulse signal") pyplot.show() output_data = pulse # saves the data into an HDF5 file with a '.hdf5' extension outdir = os.path.dirname(output) if not os.path.exists(outdir): bob.io.base.create_directories_safe(outdir) bob.io.base.save(output_data, output) logger.info("Output file saved to `%s'...", output)
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict(prog=prog, version=version,) args = docopt(__doc__ % completions, argv=arguments, version='Non-rigid motion elimination for videos (%s)' % version,) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) illumdir = get_parameter(args, configuration, 'illumdir', 'illumination') motiondir = get_parameter(args, configuration, 'motiondir', 'motion') seglength = get_parameter(args, configuration, 'seglength', 61) cutoff = get_parameter(args, configuration, 'cutoff', 0.05) save_threshold = get_parameter(args, configuration, 'save-threshold', 'threshold.txt') load_threshold = get_parameter(args, configuration, 'load-threshold', '') cvpr14 = get_parameter(args, configuration, 'cvpr14', False) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) print(load_threshold) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, args['--verbose']) # TODO: find a way to check protocol names - Guillaume HEUSCH, 22-06-2018 if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # determine the threshold for the standard deviation to be applied to the segments # this part is not executed if a threshold is provided if load_threshold == 'None': all_stds = [] for obj in objects: # load the llumination corrected signal logger.debug("Computing standard deviations in color signals from `%s'...", obj.path) illum_file = obj.make_path(illumdir, '.hdf5') try: color = bob.io.base.load(illum_file) except (IOError, RuntimeError) as e: logger.warn("Skipping file `%s' (no color signals file available)", obj.path) continue # skip this file if there are NaN ... if numpy.isnan(numpy.sum(color)): logger.warn("Skipping file `%s' (NaN in file)", obj.path) continue # get the standard deviation in the segments green_segments, __ = build_segments(color, seglength) std_green = numpy.std(green_segments, 1, ddof=1) all_stds.extend(std_green.tolist()) logger.info("Standard deviations are computed") # sort the std and find the 5% at the top to get the threshold sorted_stds = sorted(all_stds, reverse=True) cut_index = int(cutoff * len(all_stds)) + 1 threshold = sorted_stds[cut_index] logger.info("The threshold was {0} (removing {1} percent of the largest segments)".format(threshold, 100*cutoff)) # write threshold to file f = open(save_threshold, 'w') f.write(str(threshold)) f.close() else: # load threshold f = open(load_threshold, 'r') threshold = float(f.readline().rstrip()) # cut segments where the std is too large for obj in objects: # expected output file output = obj.make_path(motiondir, '.hdf5') # if output exists and not overwriting, skip this file if os.path.exists(output) and not overwrite: logger.info("Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load the color signals logger.debug("Eliminating motion in color signals from `%s'...", obj.path) illum_file = obj.make_path(illumdir, '.hdf5') try: color = bob.io.base.load(illum_file) except (IOError, RuntimeError) as e: logger.warn("Skipping file `%s' (no color signals file available)", obj.path) continue # skip this file if there are NaN ... if numpy.isnan(numpy.sum(color)): logger.warn("Skipping file `%s' (NaN in file)", obj.path) continue # divide the signals into segments green_segments, end_index = build_segments(color, seglength) # remove segments with high variability pruned_segments, gaps, cut_index = prune_segments(green_segments, threshold) # build final signal - but be sure that there are some segments left ! if pruned_segments.shape[0] == 0: logger.warn("All segments have been discared in {0}".format(obj.path)) continue if cvpr14: corrected_green = build_final_signal_cvpr14(pruned_segments, gaps) else: corrected_green = build_final_signal(pruned_segments, gaps) if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(2, sharex=True) axarr[0].plot(range(end_index), color[:end_index], 'g') xmax, xmin, ymax, ymin = axarr[0].axis() for cuts in cut_index: axarr[0].vlines(cuts[0], ymin, ymax, color='black', linewidths='2') axarr[0].vlines(cuts[1], ymin, ymax, color='black', linewidths='2') axarr[0].plot(range(cuts[0],cuts[1]), color[cuts[0]:cuts[1]], 'r') axarr[0].set_title('Original color pulse') axarr[1].plot(range(corrected_green.shape[0]), corrected_green, 'g') axarr[1].set_title('Motion corrected color pulse') pyplot.show() # saves the data into an HDF5 file with a '.hdf5' extension outputdir = os.path.dirname(output) if not os.path.exists(outputdir): bob.io.base.create_directories_safe(outputdir) bob.io.base.save(corrected_green, output) logger.info("Output file saved to `%s'...", output) return 0
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Filtering for signals (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) motiondir = get_parameter(args, configuration, 'motiondir', 'motion') pulsedir = get_parameter(args, configuration, 'pulsedir', 'pulse') Lambda = get_parameter(args, configuration, 'Lambda', 300) window = get_parameter(args, configuration, 'window', 23) framerate = get_parameter(args, configuration, 'framerate', 61) order = get_parameter(args, configuration, 'order', 128) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) gridcount = get_parameter(args, configuration, 'gridcount', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, verbosity_level) # TODO: find a way to check protocol names - Guillaume HEUSCH, 22-06-2018 if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # if we are on a grid environment, just find what I have to process. sge = False try: sge = os.environ.has_key('SGE_TASK_ID') # python2 except AttributeError: sge = 'SGE_TASK_ID' in os.environ # python3 if sge: pos = int(os.environ['SGE_TASK_ID']) - 1 if pos >= len(objects): raise RuntimeError( "Grid request for job {} on a setup with {} jobs".format( pos, len(objects))) objects = [objects[pos]] if gridcount: print(len(objects)) sys.exit() # build the bandpass filter one and for all b = build_bandpass_filter(framerate, order, plot) ################ ### LET'S GO ### ################ for obj in objects: # expected output file output = obj.make_path(pulsedir, '.hdf5') # if output exists and not overwriting, skip this file if os.path.exists(output) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load the corrected color signals of shape (3, nb_frames) logger.info("Filtering in signal from `%s'...", obj.path) motion_file = obj.make_path(motiondir, '.hdf5') try: motion_corrected_signal = bob.io.base.load(motion_file) except (IOError, RuntimeError) as e: logger.warn( "Skipping file `%s' (no motion corrected signal file available)", obj.path) continue # check whether the signal is long enough to be filtered with the bandpass of this order padlen = 3 * len(b) if motion_corrected_signal.shape[0] < padlen: logger.warn( "Skipping file {0} (unable to bandpass filter it, the signal is probably not long enough)" .format(obj.path)) continue # detrend green_detrend = detrend(motion_corrected_signal, Lambda) # average green_averaged = average(green_detrend, window) # bandpass from scipy.signal import filtfilt green_bandpassed = filtfilt(b, numpy.array([1]), green_averaged) # plot the result if plot: from matplotlib import pyplot f, ax = pyplot.subplots(4, sharex=True) ax[0].plot(range(motion_corrected_signal.shape[0]), motion_corrected_signal, 'g') ax[0].set_title('Original signal') ax[1].plot(range(motion_corrected_signal.shape[0]), green_detrend, 'g') ax[1].set_title('After detrending') ax[2].plot(range(motion_corrected_signal.shape[0]), green_averaged, 'g') ax[2].set_title('After averaging') ax[3].plot(range(motion_corrected_signal.shape[0]), green_bandpassed, 'g') ax[3].set_title('Bandpassed signal') pyplot.show() output_data = numpy.copy(green_bandpassed) # saves the data into an HDF5 file with a '.hdf5' extension pulse_outdir = os.path.dirname(output) if not os.path.exists(pulse_outdir): bob.io.base.create_directories_safe(pulse_outdir) bob.io.base.save(output_data, output) logger.info("Output file saved to `%s'...", output) return 0
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Illumination rectification for videos (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) facedir = get_parameter(args, configuration, 'facedir', 'face') bgdir = get_parameter(args, configuration, 'bgdir', 'bg') illumdir = get_parameter(args, configuration, 'illumdir', 'illumination') start = get_parameter(args, configuration, 'start', 0) end = get_parameter(args, configuration, 'end', 0) step = get_parameter(args, configuration, 'step', 0.05) length = get_parameter(args, configuration, 'length', 1) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) gridcount = get_parameter(args, configuration, 'gridcount', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, verbosity_level) # TODO: find a way to check protocol names - Guillaume HEUSCH, 22-06-2018 if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # if we are on a grid environment, just find what I have to process. sge = False try: sge = os.environ.has_key('SGE_TASK_ID') # python2 except AttributeError: sge = 'SGE_TASK_ID' in os.environ # python3 if sge: pos = int(os.environ['SGE_TASK_ID']) - 1 if pos >= len(objects): raise RuntimeError( "Grid request for job {} on a setup with {} jobs".format( pos, len(objects))) objects = [objects[pos]] if gridcount: print(len(objects)) sys.exit() # does the actual work - for every video in the available dataset, # extract the average color in both the mask area and in the backround, # and then correct face illumination by removing the global illumination for obj in objects: # expected output file output = obj.make_path(illumdir, '.hdf5') logger.debug("expected output file -> {0}".format(output)) # if output exists and not overwriting, skip this file if os.path.exists(output) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load the color signal of the face face_file = obj.make_path(facedir, '.hdf5') try: face = bob.io.base.load(face_file) except (IOError, RuntimeError) as e: logger.warn("Skipping file `%s' (no face file available)", obj.path) continue # load the color signal of the background bg_file = obj.make_path(bgdir, '.hdf5') try: bg = bob.io.base.load(bg_file) except (IOError, RuntimeError) as e: logger.warn("Skipping file `%s' (no background file available)", obj.path) continue # indices where to start and to end the processing logger.debug("Sequence length = {0}".format(face.shape[0])) start_index = start end_index = end if (end_index == 0): end_index = face.shape[0] if end_index > face.shape[0]: logger.warn("Skipping Sequence {0} : not long enough ({1})".format( obj.path, face.shape[0])) continue logger.info("Processing sequence {0} ...".format(obj.path)) # truncate the signals if needed face = face[start_index:end_index] bg = bg[start_index:end_index] logger.debug("Processing %d frames...", face.shape[0]) # apply NLMS filtering corrected_green = rectify_illumination(face, bg, step, length) if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(3, sharex=True) axarr[0].plot(range(face.shape[0]), face, 'g') axarr[0].set_title( r"$g_{face}$: average green value on the mask region") axarr[1].plot(range(bg.shape[0]), bg, 'g') axarr[1].set_title( r"$g_{bg}$: average green value on the background") axarr[2].plot(range(corrected_green.shape[0]), corrected_green, 'g') axarr[2].set_title(r"$g_{IR}$: illumination rectified signal") pyplot.show() # saves the data into an HDF5 file with a '.hdf5' extension outputdir = os.path.dirname(output) if not os.path.exists(outputdir): bob.io.base.create_directories_safe(outputdir) bob.io.base.save(corrected_green, output) logger.info("Output file saved to `%s'...", output) return 0
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Signal extractor for videos (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) pulsedir = get_parameter(args, configuration, 'pulsedir', 'pulse') npoints = get_parameter(args, configuration, 'npoints', 40) indent = get_parameter(args, configuration, 'indent', 10) quality = get_parameter(args, configuration, 'quality', 0.01) distance = get_parameter(args, configuration, 'distance', 10) framerate = get_parameter(args, configuration, 'framerate', 61) order = get_parameter(args, configuration, 'order', 128) window = get_parameter(args, configuration, 'window', 0) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) gridcount = get_parameter(args, configuration, 'gridcount', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, verbosity_level) if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # if we are on a grid environment, just find what I have to process. sge = False try: sge = os.environ.has_key('SGE_TASK_ID') # python2 except AttributeError: sge = 'SGE_TASK_ID' in os.environ # python3 if sge: pos = int(os.environ['SGE_TASK_ID']) - 1 if pos >= len(objects): raise RuntimeError( "Grid request for job {} on a setup with {} jobs".format( pos, len(objects))) objects = [objects[pos]] if gridcount: print(len(objects)) sys.exit() # build the bandpass filter one and for all bandpass_filter = build_bandpass_filter(framerate, order, plot) # does the actual work - for every video in the available dataset, # extract the signals and dumps the results to the corresponding directory for obj in objects: # expected output file output = obj.make_path(pulsedir, '.hdf5') # if output exists and not overwriting, skip this file if (os.path.exists(output)) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load video video = obj.load_video(configuration.dbdir) logger.info("Processing input video from `%s'...", video.filename) # number of frames nb_frames = len(video) # load the result of face detection bounding_boxes = obj.load_face_detection() # output data output_data = numpy.zeros(nb_frames, dtype='float64') chrom = numpy.zeros((nb_frames, 2), dtype='float64') # loop on video frames for i, frame in enumerate(video): logger.debug("Processing frame %d/%d...", i + 1, len(video)) if i == 0: # first frame: # -> load the keypoints detected by DMRF # -> infer the mask from the keypoints # -> detect the face # -> get "good features" inside the face kpts = obj.load_drmf_keypoints() mask_points, mask = kp66_to_mask(frame, kpts, int(indent), plot) try: bbox = bounding_boxes[i] except NameError: bbox, quality = bob.ip.facedetect.detect_single_face(frame) # define the face width for the whole sequence facewidth = bbox.size[1] face = crop_face(frame, bbox, facewidth) good_features = get_good_features_to_track( face, npoints, quality, distance, plot) else: # subsequent frames: # -> crop the face with the bounding_boxes of the previous frame (so # that faces are of the same size) # -> get the projection of the corners detected in the previous frame # -> find the (affine) transformation relating previous corners with # current corners # -> apply this transformation to the mask face = crop_face(frame, prev_bb, facewidth) good_features = track_features(prev_face, face, prev_features, plot) project = find_transformation(prev_features, good_features) if project is None: logger.warn( "Sequence {0}, frame {1} : No projection was found" " between previous and current frame, mask from previous frame will be used" .format(obj.path, i)) else: mask_points = get_current_mask_points(mask_points, project) # update stuff for the next frame: # -> the previous face is the face in this frame, with its bbox (and not # with the previous one) # -> the features to be tracked on the next frame are re-detected try: prev_bb = bounding_boxes[i] except NameError: bb, quality = bob.ip.facedetect.detect_single_face(frame) prev_bb = bb prev_face = crop_face(frame, prev_bb, facewidth) prev_features = get_good_features_to_track(face, npoints, quality, distance, plot) if prev_features is None: logger.warn( "Sequence {0}, frame {1} No features to track" " detected in the current frame, using the previous ones". format(obj.path, i)) prev_features = good_features # get the mask face_mask = get_mask(frame, mask_points) if plot and verbosity_level >= 2: from matplotlib import pyplot mask_image = numpy.copy(frame) mask_image[:, face_mask] = 255 pyplot.imshow(numpy.rollaxis(numpy.rollaxis(mask_image, 2), 2)) pyplot.show() # compute the mean rgb values of the pixels inside the mask r, g, b = compute_mean_rgb(frame, face_mask) # project onto the chrominance colorspace chrom[i] = project_chrominance(r, g, b) # now that we have the chrominance signals, apply bandpass from scipy.signal import filtfilt x_bandpassed = numpy.zeros(nb_frames, dtype='float64') y_bandpassed = numpy.zeros(nb_frames, dtype='float64') x_bandpassed = filtfilt(bandpass_filter, numpy.array([1]), chrom[:, 0]) y_bandpassed = filtfilt(bandpass_filter, numpy.array([1]), chrom[:, 1]) if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(2, sharex=True) axarr[0].plot(range(x_bandpassed.shape[0]), x_bandpassed, 'k') axarr[0].set_title("X bandpassed") axarr[1].plot(range(y_bandpassed.shape[0]), y_bandpassed, 'k') axarr[1].set_title("Y bandpassed") pyplot.show() # build the final pulse signal alpha = numpy.std(x_bandpassed) / numpy.std(y_bandpassed) pulse = x_bandpassed - alpha * y_bandpassed # overlap-add if window_size != 0 if int(window) > 0: window_size = int(window) window_stride = window_size / 2 for w in range(0, (len(pulse) - window_size), window_stride): pulse[w:w + window_size] = 0.0 xw = x_bandpassed[w:w + window_size] yw = y_bandpassed[w:w + window_size] alpha = numpy.std(xw) / numpy.std(yw) sw = xw - alpha * yw sw *= numpy.hanning(window_size) pulse[w:w + window_size] += sw if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(1) pyplot.plot(range(pulse.shape[0]), pulse, 'k') pyplot.title("Pulse signal") pyplot.show() output_data = pulse # saves the data into an HDF5 file with a '.hdf5' extension out_pulsedir = os.path.dirname(output) if not os.path.exists(out_pulsedir): bob.io.base.create_directories_safe(out_pulsedir) bob.io.base.save(output_data, output) logger.info("Output file saved to `%s'...", output)
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Signal extractor for videos (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) facedir = get_parameter(args, configuration, 'facedir', 'face') bgdir = get_parameter(args, configuration, 'bgdir', 'bg') npoints = get_parameter(args, configuration, 'npoints', 40) indent = get_parameter(args, configuration, 'indent', 10) quality = get_parameter(args, configuration, 'quality', 0.01) distance = get_parameter(args, configuration, 'distance', 10) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) gridcount = get_parameter(args, configuration, 'gridcount', False) wholeface = get_parameter(args, configuration, 'wholeface', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, verbosity_level) # TODO: find a way to check protocol names - Guillaume HEUSCH, 22-06-2018 if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # if we are on a grid environment, just find what I have to process. sge = False try: sge = os.environ.has_key('SGE_TASK_ID') # python2 except AttributeError: sge = 'SGE_TASK_ID' in os.environ # python3 if sge: pos = int(os.environ['SGE_TASK_ID']) - 1 if pos >= len(objects): raise RuntimeError( "Grid request for job {} on a setup with {} jobs".format( pos, len(objects))) objects = [objects[pos]] if gridcount: print(len(objects)) sys.exit() # does the actual work - for every video in the available dataset, # extract the signals and dumps the results to the corresponding directory for obj in objects: # expected output file output_face = obj.make_path(facedir, '.hdf5') output_bg = obj.make_path(bgdir, '.hdf5') # if output exists and not overwriting, skip this file if (os.path.exists(output_face) and os.path.exists(output_bg)) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output_face) continue # load video video = obj.load_video(configuration.dbdir) logger.info("Processing input video from `%s'...", video.filename) # load the result of face detection bounding_boxes = obj.load_face_detection() # average green color in the mask area face_color = numpy.zeros(len(video), dtype='float64') # average green color in the background area bg_color = numpy.zeros(len(video), dtype='float64') # loop on video frames for i, frame in enumerate(video): logger.debug("Processing frame %d/%d...", i + 1, len(video)) if i == 0: # first frame: # -> load the keypoints detected by DMRF # -> infer the mask from the keypoints # -> detect the face # -> get "good features" inside the face if not wholeface: kpts = obj.load_drmf_keypoints() mask_points, mask = kp66_to_mask(frame, kpts, indent, plot) try: bbox = bounding_boxes[i] except NameError: bbox, quality = bob.ip.facedetect.detect_single_face(frame) # define the face width for the whole sequence facewidth = bbox.size[1] face = crop_face(frame, bbox, facewidth) if not wholeface: good_features = get_good_features_to_track( face, npoints, quality, distance, plot) else: # subsequent frames: # -> crop the face with the bounding_boxes of the previous frame (so # that faces are of the same size) # -> get the projection of the corners detected in the previous frame # -> find the (affine) transformation relating previous corners with # current corners # -> apply this transformation to the mask face = crop_face(frame, prev_bb, facewidth) if not wholeface: good_features = track_features(prev_face, face, prev_features, plot) project = find_transformation(prev_features, good_features) if project is None: logger.warn( "Sequence {0}, frame {1} : No projection was found" " between previous and current frame, mask from previous frame will be used" .format(obj.path, i)) else: mask_points = get_current_mask_points( mask_points, project) # update stuff for the next frame: # -> the previous face is the face in this frame, with its bbox (and not # with the previous one) # -> the features to be tracked on the next frame are re-detected try: prev_bb = bounding_boxes[i] except NameError: bb, quality = bob.ip.facedetect.detect_single_face(frame) prev_bb = bb if not wholeface: prev_face = crop_face(frame, prev_bb, facewidth) prev_features = get_good_features_to_track( face, npoints, quality, distance, plot) if prev_features is None: logger.warn( "Sequence {0}, frame {1} No features to track" " detected in the current frame, using the previous ones" .format(obj.path, i)) prev_features = good_features # get the bottom face region average colors face_mask = get_mask(frame, mask_points) # original algorithm: green only face_color[i] = compute_average_colors_mask( frame, face_mask, plot)[1] else: face_color[i] = compute_average_colors_wholeface(face, plot) # get the background region average colors bg_mask = numpy.zeros((frame.shape[1], frame.shape[2]), dtype=bool) bg_mask[:100, :100] = True bg_color[i] = compute_average_colors_mask(frame, bg_mask, plot)[1] # saves the data into an HDF5 file with a '.hdf5' extension out_facedir = os.path.dirname(output_face) if not os.path.exists(out_facedir): bob.io.base.create_directories_safe(out_facedir) bob.io.base.save(face_color, output_face) logger.info("Output file saved to `%s'...", output_face) out_bgdir = os.path.dirname(output_bg) if not os.path.exists(out_bgdir): bob.io.base.create_directories_safe(out_bgdir) bob.io.base.save(bg_color, output_bg) logger.info("Output file saved to `%s'...", output_bg)
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Signal extractor for videos (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) pulsedir = get_parameter(args, configuration, 'pulsedir', 'pulse') hrdir = get_parameter(args, configuration, 'hrdir', 'hr') framerate = get_parameter(args, configuration, 'framerate', 61) nsegments = get_parameter(args, configuration, 'nsegments', 12) nfft = get_parameter(args, configuration, 'nfft', 8192) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, args['--verbose']) # TODO: find a way to check protocol names - Guillaume HEUSCH, 22-06-2018 if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() ################ ### LET'S GO ### ################ for obj in objects: # expected output file output = obj.make_path(hrdir, '.hdf5') # if output exists and not overwriting, skip this file if os.path.exists(output) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load the filtered color signals of shape (3, nb_frames) logger.info("Frequency analysis of color signals from `%s'...", obj.path) filtered_file = obj.make_path(pulsedir, '.hdf5') try: signal = bob.io.base.load(filtered_file) except (IOError, RuntimeError) as e: logger.warn("Skipping file `%s' (no color signals file available)", obj.path) continue if plot: from matplotlib import pyplot pyplot.plot(range(signal.shape[0]), signal, 'g') pyplot.title('Filtered green signal') pyplot.show() # find the segment length, such that we have 8 50% overlapping segments (Matlab's default) segment_length = (2 * signal.shape[0]) // (nsegments + 1) # the number of points for FFT should be larger than the segment length ... if nfft < segment_length: logger.warn("Skipping file `%s' (nfft < nperseg)", obj.path) continue from scipy.signal import welch green_f, green_psd = welch(signal, framerate, nperseg=segment_length, nfft=nfft) # find the max of the frequency spectrum in the range of interest first = numpy.where(green_f > 0.7)[0] last = numpy.where(green_f < 4)[0] first_index = first[0] last_index = last[-1] range_of_interest = range(first_index, last_index + 1, 1) max_idx = numpy.argmax(green_psd[range_of_interest]) f_max = green_f[range_of_interest[max_idx]] hr = f_max * 60.0 logger.info("Heart rate = {0}".format(hr)) if plot: from matplotlib import pyplot pyplot.semilogy(green_f, green_psd, 'g') xmax, xmin, ymax, ymin = pyplot.axis() pyplot.vlines(green_f[range_of_interest[max_idx]], ymin, ymax, color='red') pyplot.title( 'Power spectrum of the green signal (HR = {0:.1f})'.format(hr)) pyplot.show() output_data = numpy.array([hr], dtype='float64') # saves the data into an HDF5 file with a '.hdf5' extension outdir = os.path.dirname(output) if not os.path.exists(outdir): bob.io.base.create_directories_safe(outdir) bob.io.base.save(output_data, output) logger.info("Output file saved to `%s'...", output) return 0
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Signal extractor for videos (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) pulsedir = get_parameter(args, configuration, 'pulsedir', 'pulse') npoints = get_parameter(args, configuration, 'npoints', 40) indent = get_parameter(args, configuration, 'indent', 10) quality = get_parameter(args, configuration, 'quality', 0.01) distance = get_parameter(args, configuration, 'distance', 10) stride = get_parameter(args, configuration, 'stride', 61) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) gridcount = get_parameter(args, configuration, 'gridcount', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, verbosity_level) if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() # if we are on a grid environment, just find what I have to process. sge = False try: sge = os.environ.has_key('SGE_TASK_ID') # python2 except AttributeError: sge = 'SGE_TASK_ID' in os.environ # python3 if sge: pos = int(os.environ['SGE_TASK_ID']) - 1 if pos >= len(objects): raise RuntimeError( "Grid request for job {} on a setup with {} jobs".format( pos, len(objects))) objects = [objects[pos]] if gridcount: print(len(objects)) # the temporal stride temporal_stride = stride # does the actual work - for every video in the available dataset, # extract the signals and dumps the results to the corresponding directory for obj in objects: # expected output file output = obj.make_path(pulsedir, '.hdf5') # if output exists and not overwriting, skip this file if (os.path.exists(output)) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load video video = obj.load_video(configuration.dbdir) logger.info("Processing input video from `%s'...", video.filename) nb_final_frames = len(video) # load the result of face detection bounding_boxes = obj.load_face_detection() # the result -> the pulse signal output_data = numpy.zeros(nb_final_frames, dtype='float64') # store the eigenvalues and the eigenvectors at each frame eigenvalues = numpy.zeros((3, nb_final_frames), dtype='float64') eigenvectors = numpy.zeros((3, 3, nb_final_frames), dtype='float64') # loop on video frames for i, frame in enumerate(video): logger.debug("Processing frame %d/%d...", i + 1, len(video)) if i == 0: # first frame: # -> load the keypoints detected by DMRF # -> infer the mask from the keypoints # -> detect the face # -> get "good features" inside the face kpts = obj.load_drmf_keypoints() mask_points, mask = kp66_to_mask(frame, kpts, indent, plot) try: bbox = bounding_boxes[i] except NameError: bbox, quality = bob.ip.facedetect.detect_single_face(frame) # define the face width for the whole sequence facewidth = bbox.size[1] face = crop_face(frame, bbox, facewidth) good_features = get_good_features_to_track( face, npoints, quality, distance, plot) else: # subsequent frames: # -> crop the face with the bounding_boxes of the previous frame (so # that faces are of the same size) # -> get the projection of the corners detected in the previous frame # -> find the (affine) transformation relating previous corners with # current corners # -> apply this transformation to the mask face = crop_face(frame, prev_bb, facewidth) good_features = track_features(prev_face, face, prev_features, plot) project = find_transformation(prev_features, good_features) if project is None: logger.warn( "Sequence {0}, frame {1} : No projection was found" " between previous and current frame, mask from previous frame will be used" .format(obj.path, i)) else: mask_points = get_current_mask_points(mask_points, project) # update stuff for the next frame: # -> the previous face is the face in this frame, with its bbox (and not # with the previous one) # -> the features to be tracked on the next frame are re-detected try: prev_bb = bounding_boxes[i] except NameError: bb, quality = bob.ip.facedetect.detect_single_face(frame) prev_bb = bb prev_face = crop_face(frame, prev_bb, facewidth) prev_features = get_good_features_to_track(face, npoints, quality, distance, plot) if prev_features is None: logger.warn( "Sequence {0}, frame {1} No features to track" " detected in the current frame, using the previous ones". format(obj.path, i)) prev_features = good_features # get the bottom face region face_mask = get_mask(frame, mask_points) if plot: from matplotlib import pyplot mask_image = numpy.copy(frame) mask_image[:, face_mask] = 255 pyplot.title("mask pixels in frame {0}".format(i)) pyplot.imshow(numpy.rollaxis(numpy.rollaxis(mask_image, 2), 2)) pyplot.show() # get the skin pixels inside the region skin_pixels = frame[:, face_mask] skin_pixels = skin_pixels.astype('float64') / 255.0 # build c matrix and get eigenvectors and eigenvalues eigenvalues[:, i], eigenvectors[:, :, i] = get_eigen(skin_pixels) # plot the cluster of skin pixels and eigenvectors (see Figure 1) if plot and verbosity_level >= 2: plot_eigenvectors(skin_pixels, eigenvectors[:, :, i]) # build P and add it to the pulse signal if i >= temporal_stride: tau = i - temporal_stride p = build_P(i, int(stride), eigenvectors, eigenvalues) output_data[tau:i] += (p - numpy.mean(p)) # plot the pulse signal if plot: import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111) ax.plot(range(nb_final_frames), output_data) plt.show() # saves the data into an HDF5 file with a '.hdf5' extension outdir = os.path.dirname(output) if not os.path.exists(outdir): bob.io.base.create_directories_safe(outdir) bob.io.base.save(output_data, output) logger.info("Output file saved to `%s'...", output) return 0
def main(user_input=None): # Parse the command-line arguments if user_input is not None: arguments = user_input else: arguments = sys.argv[1:] prog = os.path.basename(sys.argv[0]) completions = dict( prog=prog, version=version, ) args = docopt( __doc__ % completions, argv=arguments, version='Signal extractor for videos (%s)' % version, ) # load configuration file configuration = load([os.path.join(args['<configuration>'])]) # get various parameters, either from config file or command-line protocol = get_parameter(args, configuration, 'protocol', 'all') subset = get_parameter(args, configuration, 'subset', None) skindir = get_parameter(args, configuration, 'skindir', 'skin') threshold = get_parameter(args, configuration, 'threshold', 0.5) skininit = get_parameter(args, configuration, 'skininit', False) overwrite = get_parameter(args, configuration, 'overwrite', False) plot = get_parameter(args, configuration, 'plot', False) gridcount = get_parameter(args, configuration, 'gridcount', False) verbosity_level = get_parameter(args, configuration, 'verbose', 0) print(protocol) print(type(protocol)) # if the user wants more verbosity, lowers the logging level from bob.core.log import set_verbosity_level set_verbosity_level(logger, verbosity_level) if hasattr(configuration, 'database'): objects = configuration.database.objects(protocol, subset) else: logger.error("Please provide a database in your configuration file !") sys.exit() print(objects) # if we are on a grid environment, just find what I have to process. sge = False try: sge = os.environ.has_key('SGE_TASK_ID') # python2 except AttributeError: sge = 'SGE_TASK_ID' in os.environ # python3 if sge: pos = int(os.environ['SGE_TASK_ID']) - 1 if pos >= len(objects): raise RuntimeError( "Grid request for job {} on a setup with {} jobs".format( pos, len(objects))) objects = [objects[pos]] if gridcount: print(len(objects)) sys.exit() # does the actual work - for every video in the available dataset, # extract the average color in both the mask area and in the backround, # and then correct face illumination by removing the global illumination for obj in objects: # expected output face file output = obj.make_path(skindir, '.hdf5') # if output exists and not overwriting, skip this file if os.path.exists(output) and not overwrite: logger.info( "Skipping output file `%s': already exists, use --overwrite to force an overwrite", output) continue # load the video sequence into a reader video = obj.load_video(configuration.dbdir) logger.info("Processing input video from `%s'...", video.filename) logger.debug("Sequence length = {0}".format(video.number_of_frames)) # load the result of face detection bounding_boxes = obj.load_face_detection() # average colors of the skin color skin_filter = bob.ip.skincolorfilter.SkinColorFilter() skin_colors = numpy.zeros((len(video), 3), dtype='float64') ################ ### LET'S GO ### ################ for i, frame in enumerate(video): logger.debug("Processing frame %d / %d...", i, len(video)) facewidth = bounding_boxes[i].size[1] face = crop_face(frame, bounding_boxes[i], facewidth) # skin filter if i == 0 or bool(skininit): skin_filter.estimate_gaussian_parameters(face) logger.debug( "Skin color parameters:\nmean\n{0}\ncovariance\n{1}". format(skin_filter.mean, skin_filter.covariance)) skin_mask = skin_filter.get_skin_mask(face, threshold) if plot and i == 0: from matplotlib import pyplot skin_mask_image = numpy.copy(face) skin_mask_image[:, skin_mask] = 255 pyplot.imshow( numpy.rollaxis(numpy.rollaxis(skin_mask_image, 2), 2)) pyplot.show() if numpy.count_nonzero(skin_mask) != 0: # green only skin_colors[i] = compute_average_colors_mask(face, skin_mask)[1] else: logger.warn( "No skin pixels detected in frame {0}, using previous value" .format(i)) if i == 0: skin_colors[i] = project_chrominance(128., 128., 128.) else: skin_colors[i] = skin_colors[i - 1] if plot: from matplotlib import pyplot f, axarr = pyplot.subplots(3, sharex=True) axarr[0].plot(range(skin_colors.shape[0]), skin_colors[:, 0], 'r') axarr[0].set_title("Average red value of the skin pixels") axarr[1].plot(range(skin_colors.shape[0]), skin_colors[:, 1], 'g') axarr[1].set_title("Average green value of the skin pixels") axarr[2].plot(range(skin_colors.shape[0]), skin_colors[:, 2], 'b') axarr[2].set_title("Average blue value of the skin pixels") pyplot.show() # saves the data into an HDF5 file with a '.hdf5' extension outdir = os.path.dirname(output) if not os.path.exists(outdir): bob.io.base.create_directories_safe(outdir) bob.io.base.save(skin_colors[:, 1], output) logger.info("Output file saved to `%s'...", output) return 0