Example #1
0
    def fast_search(self, chunk):
        """
        Experimental search
        Use Euler's method with known relation between cq and vmaf
        Formula -ln(1-score/100) = vmaf_cq_deriv*last_q + constant
        constant = -ln(1-score/100) - vmaf_cq_deriv*last_q
        Formula -ln(1-project.vmaf_target/100) = vmaf_cq_deriv*cq + constant
        cq = (-ln(1-project.vmaf_target/100) - constant)/vmaf_cq_deriv
        """
        vmaf_cq = []
        q_list = []

        # Make middle probe
        middle_point = (self.min_q + self.max_q) // 2
        q_list.append(middle_point)
        last_q = middle_point

        score = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, last_q))
        vmaf_cq.append((score, last_q))

        vmaf_cq_deriv = -0.18
        next_q = int(
            round(last_q + (VMAF.transform_vmaf(self.target) -
                            VMAF.transform_vmaf(score)) / vmaf_cq_deriv))

        # Clamp
        if next_q < self.min_q:
            next_q = self.min_q
        if self.max_q < next_q:
            next_q = self.max_q

        # Single probe cq guess or exit to avoid divide by zero
        if self.probes == 1 or next_q == last_q:
            self.log_probes(vmaf_cq, chunk.frames, chunk.name, next_q,
                            self.target)
            return next_q

        # Second probe at guessed value
        score_2 = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, next_q))

        # Calculate slope
        vmaf_cq_deriv = (VMAF.transform_vmaf(score_2) -
                         VMAF.transform_vmaf(score)) / (next_q - last_q)

        # Same deal different slope
        next_q = int(
            round(next_q + (VMAF.transform_vmaf(self.target) -
                            VMAF.transform_vmaf(score_2)) / vmaf_cq_deriv))

        # Clamp
        if next_q < self.min_q:
            next_q = self.min_q
        if self.max_q < next_q:
            next_q = self.max_q

        self.log_probes(vmaf_cq, chunk.frames, chunk.name, next_q, self.target)

        return next_q
Example #2
0
def per_shot_target_quality(chunk: Chunk, project: Project):
    vmaf_cq = []
    frames = chunk.frames

    # get_scene_scores(chunk, project.ffmpeg_pipe)

    # Adapt probing rate
    if project.probing_rate in (1, 2):
        probing_rate = project.probing_rate
    else:
        probing_rate = adapt_probing_rate(project.probing_rate, frames)

    q_list = []
    score = 0

    # Make middle probe
    middle_point = (project.min_q + project.max_q) // 2
    q_list.append(middle_point)
    last_q = middle_point

    score = VMAF.read_weighted_vmaf(
        vmaf_probe(chunk, last_q, project, probing_rate))
    vmaf_cq.append((score, last_q))

    if project.probes < 3:
        #Use Euler's method with known relation between cq and vmaf
        vmaf_cq_deriv = -0.18
        ## Formula -ln(1-score/100) = vmaf_cq_deriv*last_q + constant
        #constant = -ln(1-score/100) - vmaf_cq_deriv*last_q
        ## Formula -ln(1-project.vmaf_target/100) = vmaf_cq_deriv*cq + constant
        #cq = (-ln(1-project.vmaf_target/100) - constant)/vmaf_cq_deriv
        next_q = int(
            round(last_q + (VMAF.transform_vmaf(project.target_quality) -
                            VMAF.transform_vmaf(score)) / vmaf_cq_deriv))

        #Clamp
        if next_q < project.min_q:
            next_q = project.min_q
        if project.max_q < next_q:
            next_q = project.max_q

        #Single probe cq guess or exit to avoid divide by zero
        if project.probes == 1 or next_q == last_q:
            return next_q

        #Second probe at guessed value
        score_2 = VMAF.read_weighted_vmaf(
            vmaf_probe(chunk, next_q, project, probing_rate))

        #Calculate slope
        vmaf_cq_deriv = (VMAF.transform_vmaf(score_2) -
                         VMAF.transform_vmaf(score)) / (next_q - last_q)

        #Same deal different slope
        next_q = int(
            round(next_q + (VMAF.transform_vmaf(project.target_quality) -
                            VMAF.transform_vmaf(score_2)) / vmaf_cq_deriv))

        #Clamp
        if next_q < project.min_q:
            next_q = project.min_q
        if project.max_q < next_q:
            next_q = project.max_q

        return next_q

    # Initialize search boundary
    vmaf_lower = score
    vmaf_upper = score
    vmaf_cq_lower = last_q
    vmaf_cq_upper = last_q

    # Branch
    if score < project.target_quality:
        next_q = project.min_q
        q_list.append(project.min_q)
    else:
        next_q = project.max_q
        q_list.append(project.max_q)

    # Edge case check
    score = VMAF.read_weighted_vmaf(
        vmaf_probe(chunk, next_q, project, probing_rate))
    vmaf_cq.append((score, next_q))

    if next_q == project.min_q and score < project.target_quality:
        log(f"Chunk: {chunk.name}, Rate: {probing_rate}, Fr: {frames}\n"
            f"Q: {sorted([x[1] for x in vmaf_cq])}, Early Skip Low CQ\n"
            f"Vmaf: {sorted([x[0] for x in vmaf_cq], reverse=True)}\n"
            f"Target Q: {vmaf_cq[-1][1]} VMAF: {round(vmaf_cq[-1][0], 2)}\n\n")
        return next_q

    elif next_q == project.max_q and score > project.target_quality:
        log(f"Chunk: {chunk.name}, Rate: {probing_rate}, Fr: {frames}\n"
            f"Q: {sorted([x[1] for x in vmaf_cq])}, Early Skip High CQ\n"
            f"Vmaf: {sorted([x[0] for x in vmaf_cq], reverse=True)}\n"
            f"Target Q: {vmaf_cq[-1][1]} VMAF: {round(vmaf_cq[-1][0], 2)}\n\n")
        return next_q

    # Set boundary
    if score < project.target_quality:
        vmaf_lower = score
        vmaf_cq_lower = next_q
    else:
        vmaf_upper = score
        vmaf_cq_upper = next_q

    # VMAF search
    for _ in range(project.probes - 2):
        new_point = weighted_search(vmaf_cq_lower, vmaf_lower, vmaf_cq_upper,
                                    vmaf_upper, project.target_quality)
        if new_point in [x[1] for x in vmaf_cq]:
            break

        q_list.append(new_point)
        score = VMAF.read_weighted_vmaf(
            vmaf_probe(chunk, new_point, project, probing_rate))
        vmaf_cq.append((score, new_point))

        # Update boundary
        if score < project.target_quality:
            vmaf_lower = score
            vmaf_cq_lower = new_point
        else:
            vmaf_upper = score
            vmaf_cq_upper = new_point

    q, q_vmaf = get_target_q(vmaf_cq, project.target_quality)

    log(f'Chunk: {chunk.name}, Rate: {probing_rate}, Fr: {frames}\n'
        f'Q: {sorted([x[1] for x in vmaf_cq])}\n'
        f'Vmaf: {sorted([x[0] for x in vmaf_cq], reverse=True)}\n'
        f'Target Q: {q} VMAF: {round(q_vmaf, 2)}\n\n')

    # Plot Probes
    if project.vmaf_plots and len(vmaf_cq) > 3:
        plot_probes(project, vmaf_cq, chunk, frames)

    return q
Example #3
0
    def per_shot_target_quality(self, chunk: Chunk):
        """
        :type: Chunk chunk to probe
        :rtype: int q to use
        """
        vmaf_cq = []
        frames = chunk.frames
        self.probing_rate = adapt_probing_rate(self.probing_rate, frames)

        q_list = []

        # Make middle probe
        middle_point = (self.min_q + self.max_q) // 2
        q_list.append(middle_point)
        last_q = middle_point

        score = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, last_q))
        vmaf_cq.append((score, last_q))

        # Initialize search boundary
        vmaf_lower = score
        vmaf_upper = score
        vmaf_cq_lower = last_q
        vmaf_cq_upper = last_q

        # Branch
        if score < self.target:
            next_q = self.min_q
            q_list.append(self.min_q)
        else:
            next_q = self.max_q
            q_list.append(self.max_q)

        # Edge case check
        score = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, next_q))
        vmaf_cq.append((score, next_q))

        if (next_q == self.min_q
                and score < self.target) or (next_q == self.max_q
                                             and score > self.target):
            self.log_probes(
                vmaf_cq,
                frames,
                chunk.name,
                next_q,
                score,
                skip="low" if score < self.target else "high",
            )
            return next_q

        # Set boundary
        if score < self.target:
            vmaf_lower = score
            vmaf_cq_lower = next_q
        else:
            vmaf_upper = score
            vmaf_cq_upper = next_q

        # VMAF search
        for _ in range(self.probes - 2):
            new_point = self.weighted_search(vmaf_cq_lower, vmaf_lower,
                                             vmaf_cq_upper, vmaf_upper,
                                             self.target)
            if new_point in [x[1] for x in vmaf_cq]:
                break

            q_list.append(new_point)
            score = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, new_point))
            vmaf_cq.append((score, new_point))

            # Update boundary
            if score < self.target:
                vmaf_lower = score
                vmaf_cq_lower = new_point
            else:
                vmaf_upper = score
                vmaf_cq_upper = new_point

        q, q_vmaf = self.get_target_q(vmaf_cq, self.target)
        self.log_probes(vmaf_cq, frames, chunk.name, q, q_vmaf)

        # Plot Probes
        if self.make_plots and len(vmaf_cq) > 3:
            self.plot_probes(vmaf_cq, chunk, frames)

        return q
Example #4
0
    def per_shot_target_quality(self, chunk: Chunk):
        # Refactor this mess
        vmaf_cq = []
        frames = chunk.frames

        if self.probing_rate not in (1, 2):
            self.probing_rate = self.adapt_probing_rate(
                self.probing_rate, frames)

        if self.probes < 3:
            return self.fast_search(chunk)

        q_list = []
        score = 0

        # Make middle probe
        middle_point = (self.min_q + self.max_q) // 2
        q_list.append(middle_point)
        last_q = middle_point

        score = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, last_q))
        vmaf_cq.append((score, last_q))

        # Initialize search boundary
        vmaf_lower = score
        vmaf_upper = score
        vmaf_cq_lower = last_q
        vmaf_cq_upper = last_q

        # Branch
        if score < self.target:
            next_q = self.min_q
            q_list.append(self.min_q)
        else:
            next_q = self.max_q
            q_list.append(self.max_q)

        # Edge case check
        score = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, next_q))
        vmaf_cq.append((score, next_q))

        if next_q == self.min_q and score < self.target:
            self.log_probes(vmaf_cq, frames, chunk.name, skip='low')
            return next_q
        elif next_q == self.max_q and score > self.target:
            self.log_probes(vmaf_cq, frames, chunk.name, skip='high')
            return next_q

        # Set boundary
        if score < self.target:
            vmaf_lower = score
            vmaf_cq_lower = next_q
        else:
            vmaf_upper = score
            vmaf_cq_upper = next_q

        # VMAF search
        for _ in range(self.probes - 2):
            new_point = self.weighted_search(vmaf_cq_lower, vmaf_lower,
                                             vmaf_cq_upper, vmaf_upper,
                                             self.target)
            if new_point in [x[1] for x in vmaf_cq]:
                break

            q_list.append(new_point)
            score = VMAF.read_weighted_vmaf(self.vmaf_probe(chunk, new_point))
            vmaf_cq.append((score, new_point))

            # Update boundary
            if score < self.target:
                vmaf_lower = score
                vmaf_cq_lower = new_point
            else:
                vmaf_upper = score
                vmaf_cq_upper = new_point

        q, q_vmaf = self.get_target_q(vmaf_cq, self.target)
        self.log_probes(vmaf_cq, frames, chunk.name, q=q, q_vmaf=q_vmaf)
        # log(f'Scene_score {self.get_scene_scores(chunk, self.ffmpeg_pipe)}')
        # Plot Probes
        if self.make_plots and len(vmaf_cq) > 3:
            self.plot_probes(vmaf_cq, chunk, frames)

        return q