コード例 #1
0
def plot_vmaf_graph(output_path, frame_nums, frame_scores, source_duration, lowest_values, fps, title=""):

    timecode = Timecode(fps, '00:00:00:00')

    plt.title(title)

    # generate major tics based on evenly divided time

    chosen_frames = np.linspace(0, len(frame_nums) - 1, 20)
    ticframes = [frame_nums[int(i)] for i in chosen_frames]
    ticlabels = [timecode.tc_to_string(
        *timecode.frames_to_tc(ticframe)) for ticframe in ticframes]
    plt.xticks(ticframes, ticlabels, rotation='vertical')

    plt.plot(frame_nums, frame_scores)

    ax = plt.axes()

    if lowest_values != None:
        style = dict(size=10, color='gray')
        # label the valleys
        for idx, lowval in enumerate(lowest_values):
            ax.text(lowval, frame_scores[lowval] - 5, str(idx + 1), **style)

    ax.set_xticks(ticframes, minor=True)

    ax.grid()

    plt.ylabel('vmaf score')
    plt.ylim(0, 100)
    plt.xlabel('time')
    plt.subplots_adjust(bottom=0.3)
    plt.gcf().set_size_inches(9, 5)
    plt.savefig(output_path)
    plt.clf()
コード例 #2
0
def plot_multi_vmaf_timegraph(output_path, frame_nums, baseline_frame_scores, variant_list, target_bitrate, source_duration, fps, test_item='bitrate'):

    timecode = Timecode(fps, '00:00:00:00')

    title = " Effect of Bitrate Changes on VMAF over Asset Duration"
    plt.suptitle(title, fontsize=14, color='blue')
    plt.title("Blue line is VMAF/time of existing VOD transcode (4sec GOPs).  Gray lines are quality/time for 2sec GOPs at various bitrates",
              fontsize=7, color='black')

    upper = max(baseline_frame_scores)
    lower = min(baseline_frame_scores)

    higher_bitrate_scores = [variant['vmaf_frame_scores']
                             for variant in variant_list if variant[test_item] > target_bitrate]

    lower_bitrate_scores = [variant['vmaf_frame_scores']
                            for variant in variant_list if variant[test_item] < target_bitrate]

    # generate lighter lineshades
    higher_lineshades = np.linspace(0.7, 0.9, len(higher_bitrate_scores))
    for idx, frame_scores in enumerate(higher_bitrate_scores):
        upper = max(upper, max(frame_scores))
        lower = min(lower, min(frame_scores))
        plt.plot(frame_nums, frame_scores, str(higher_lineshades[idx]))

    lower_lineshades = np.linspace(0.9, 0.7, len(lower_bitrate_scores))
    for idx, frame_scores in enumerate(lower_bitrate_scores):
        upper = max(upper, max(frame_scores))
        lower = min(lower, min(frame_scores))
        # plt.plot(frame_nums, frame_scores, str(1 - (0.1 * (1 + idx))))
        plt.plot(frame_nums, frame_scores, str(lower_lineshades[idx]))

    plt.plot(frame_nums, baseline_frame_scores)

    # generate major tics based on evenly divided time

    chosen_frames = np.linspace(0, len(frame_nums) - 1, 20)
    ticframes = [frame_nums[int(i)] for i in chosen_frames]
    ticlabels = [timecode.tc_to_string(
        *timecode.frames_to_tc(ticframe)) for ticframe in ticframes]

    plt.xticks(ticframes, ticlabels, rotation='vertical')

    ax = plt.axes()
    # style = dict(size=10, color='gray')
    # # label the valleys
    # for idx, lowval in enumerate(lowest_values):
    #     ax.text(lowval, frame_scores[lowval] - 5, str(idx + 1), **style)

    ax.set_xticks(ticframes, minor=True)

    ax.grid()

    plt.ylabel('vmaf score')
    plt.ylim(lower, upper)
    plt.xlabel('time')
    plt.subplots_adjust(bottom=0.3)
    plt.gcf().set_size_inches(15, 5)
    plt.savefig(output_path)
    plt.clf()
コード例 #3
0
def buildDatetimeFromCinedekMetadata(docroot, timecodeTag, prevDay=False, dateTag="dateTaken"):
    # type: (ET.Element, str, bool, str) -> datetime.datetime
    # The Cinedek metadata stores only the date at the end of the recorded file.  If the recording passes thru
    # midight, set prevDay to True, if you need the correct date for the starting timecode.

    timecode = Timecode(CINEDEK_FRAME_RATE,
                        docroot.find(timecodeTag).text)
    endDateStr = docroot.find(dateTag).text

    # We do a few contortions to get the timecode and date converted to a format that datetime likes. The fractional
    # seconds have to be expressed as a zero padded integer number of microseconds.
    hour, min, sec, frame = timecode.frames_to_tc(timecode.frames)
    frameInMicrosec = str(int(round(1000000*(frame/CINEDEK_FRAME_RATE),0))).zfill(6)
    fullDatetime = datetime.datetime.strptime("%s %02d:%02d:%02d.%s" % (endDateStr, hour, min, sec, frameInMicrosec),
                                             "%Y%m%d %H:%M:%S.%f")
    if prevDay:
        fullDatetime = fullDatetime - datetime.timedelta(days=1)
    return fullDatetime
コード例 #4
0
def ffmpeg_grab_frame_triptychs_slow(frame_nums, source_path, transcode_path,
                                     results_directory, transcode_fps,
                                     dest_size):

    timecode = Timecode(transcode_fps, '00:00:00:00')
    return_frames = []
    source_frames = ffmpeg_grab_frames(local_source, frame_nums,
                                       results_directory, "source_")
    transcode_frames = ffmpeg_grab_frames(local_transcode, frame_nums,
                                          results_directory, "transcode_")

    diff_frames = ffmpeg_grab_diff_frames(frame_nums, source_path,
                                          transcode_path, results_directory,
                                          transcode_fps, dest_size)

    for (frame_num, source, transcode, diff) in zip(frame_nums, source_frames,
                                                    transcode_frames,
                                                    diff_frames):

        frame_time = timecode.tc_to_string(*timecode.frames_to_tc(frame_num))
        return_path = frame_time.replace(":", "-") + ".jpg"
        return_abs_path = os.path.join(results_directory, return_path)

        source_full_path = os.path.join(results_directory, source)
        transcode_full_path = os.path.join(results_directory, transcode)
        diff_full_path = os.path.join(results_directory, diff)

        ffmpeg_htile_images(source_full_path, transcode_full_path,
                            diff_full_path, return_abs_path)

        os.remove(source_full_path)
        os.remove(transcode_full_path)
        os.remove(diff_full_path)

        return_frames.append(return_path)

    return return_frames
コード例 #5
0
def plot_three_vmaf_timegraph(output_path, frame_nums, baseline_frame_scores, variant_list, target_bitrate, recommended_bitrate, source_duration, fps):

    timecode = Timecode(fps, '00:00:00:00')

    upper = max(baseline_frame_scores)
    lower = min(baseline_frame_scores)

    baseline_label = "Existing " + \
        str(target_bitrate) + "bps VOD Transcode (4sec GOP)"
    identical_label = "Identical " + \
        str(target_bitrate) + "bps but with 2sec GOP"
    recommended_label = str(
        target_bitrate) + "bps transcode 2 sec GOP that should match VOD quality"

    # sift through all of the test variants and find the variant that matches the reference bitrate:
    same_bitrate_variant = [variant['vmaf_frame_scores']
                            for variant in variant_list if variant['bitrate'] == target_bitrate][0]
    recommended_bitrate_variant = [variant['vmaf_frame_scores']
                                   for variant in variant_list if variant['bitrate'] == recommended_bitrate][0]

    title = " VOD Transcode (4sec GOP) VMAF vs. Matching Bitrate (2sec GOP) Over Asset Duration"
    plt.title(title, fontsize=14, color='blue')

    upper = max(upper, max(same_bitrate_variant))
    lower = min(lower, min(same_bitrate_variant))
    plt.plot(frame_nums, same_bitrate_variant, color='r',
             label=identical_label)

    upper = max(upper, max(recommended_bitrate_variant))
    lower = min(lower, min(recommended_bitrate_variant))
    plt.plot(frame_nums, recommended_bitrate_variant, color='g',
             label=recommended_label)

    upper = max(baseline_frame_scores)
    lower = min(baseline_frame_scores)
    plt.plot(frame_nums, baseline_frame_scores, color='k',
             label=baseline_label)

    # generate major tics based on evenly divided time

    chosen_frames = np.linspace(0, len(frame_nums) - 1, 20)
    ticframes = [frame_nums[int(i)] for i in chosen_frames]
    ticlabels = [timecode.tc_to_string(
        *timecode.frames_to_tc(ticframe)) for ticframe in ticframes]

    plt.xticks(ticframes, ticlabels, rotation='vertical')

    ax = plt.axes()
    # style = dict(size=10, color='gray')
    # # label the valleys
    # for idx, lowval in enumerate(lowest_values):
    #     ax.text(lowval, frame_scores[lowval] - 5, str(idx + 1), **style)

    ax.set_xticks(ticframes, minor=True)

    ax.grid()

    red_line = mlines.Line2D([], [], color='k', label=baseline_label)
    yellow_line = mlines.Line2D([], [], color='r', label=identical_label)
    green_line = mlines.Line2D([], [], color='g', label=recommended_label)
    plt.legend(handles=[red_line, yellow_line,
                        green_line], loc='lower right')

    plt.ylabel('vmaf score')
    plt.ylim(lower, upper)
    plt.xlabel('time')
    plt.subplots_adjust(bottom=0.3)
    plt.gcf().set_size_inches(15, 5)
    plt.savefig(output_path)
    plt.clf()
コード例 #6
0
def create_quality_report(media_id, local_source, local_transcode,
                          results_directory, subsample):

    report_data = {}
    test_data = []
    report_data['title'] = media_id
    report_data['source_file'] = local_source
    report_data['transcode_file'] = local_transcode

    source_dimensions = ffprobe_get_frame_size(local_transcode)
    transcode_dimensions = ffprobe_get_frame_size(local_transcode)
    transcode_framerate = ffprobe_get_framerate(local_transcode)

    source_video_duration = ffprobe_get_video_duration(local_source)
    source_audio_duration = ffprobe_get_audio_duration(local_source)
    transcode_video_duration = ffprobe_get_video_duration(local_transcode)
    transcode_audio_duration = ffprobe_get_audio_duration(local_transcode)
    test_data.append(
        test_video_expected_duration_match(source_video_duration,
                                           transcode_video_duration))
    test_data.append(
        test_audio_expected_duration_match(source_audio_duration,
                                           transcode_audio_duration))
    test_data.append(
        test_audio_video_duration_match(transcode_audio_duration,
                                        transcode_video_duration))

    timecode = Timecode(transcode_framerate, '00:00:00;00')

    source_loudness, source_true_peak = ffmpeg_get_loudness(local_source)
    test_data.append(test_source_loudness(source_loudness, source_true_peak))

    transcode_loudness, transcode_true_peak = ffmpeg_get_loudness(
        local_transcode)
    test_data.append(
        test_transcode_loudness(transcode_loudness, transcode_true_peak))

    report_data['test_data'] = test_data

    json_path = media_id + "_vmaf.json"

    scenes = []
    # scenes, durations = ffmepg_scene_detect(
    #    local_source, 0.5, source_duration)

    # save time if we have this file from a previous run -- for debugging
    if os.path.exists(os.path.join(results_directory, json_path)):
        score, frames = parse_vmaf_json(
            os.path.join(results_directory, json_path))
    else:
        score, frames = ffmpeg_run_vmaf_full(
            local_source, local_transcode,
            os.path.join(results_directory, json_path), transcode_framerate,
            transcode_dimensions, subsample)

    test_data.append(test_program_VMAF(score))

    frame_nums, frame_scores = zip(*frames)

    lowest_frames = find_valleys(
        frame_scores,
        (source_video_duration * float(timecode.framerate)) / subsample)

    image_path = "vmaf.png"
    plot_vmaf_graph(os.path.join(results_directory,
                                 image_path), frame_nums, frame_scores,
                    source_video_duration, lowest_frames, timecode)

    low_frames = []

    try:
        frame_triptychs = ffmpeg_grab_frame_triptychs_fast(
            lowest_frames, local_source, local_transcode, results_directory,
            transcode_framerate, transcode_dimensions)
    except:
        frame_triptychs = ffmpeg_grab_frame_triptychs_slow(
            lowest_frames, local_source, local_transcode, results_directory,
            transcode_framerate, transcode_dimensions)

    for idx, low_frame in enumerate(lowest_frames):
        low_frame_dict = {}
        low_frame_dict['index'] = 1 + idx
        low_frame_dict['frame_grab'] = frame_triptychs[idx]
        low_frame_dict['frame_time'] = timecode.tc_to_string(
            *timecode.frames_to_tc(low_frame))
        low_frame_dict['frame_vmaf'] = frame_scores[low_frame]
        low_frames.append(low_frame_dict)

    report_data['vmaf_score'] = score
    report_data['vmaf_graph'] = image_path
    duration_tc = timecode.frames_to_tc(
        timecode.float_to_tc(source_video_duration))
    report_data['duration'] = timecode.tc_to_string(*duration_tc)
    report_data['full_vmaf_json'] = json_path
    report_data['low_frames'] = low_frames

    # write the unformatted json version of our results
    write_dict_to_json(report_data,
                       os.path.join(results_directory, media_id + "_QC.json"))

    # write the formatted html version of our results

    write_html_report(
        report_data, os.path.join(results_directory,
                                  media_id + "_quality.html"))