from abc import ABCMeta, abstractmethod
import os
from vmaf.tools.misc import make_absolute_path, run_process
from vmaf.tools.stats import ListStats

__copyright__ = "Copyright 2016-2018, Netflix, Inc."
__license__ = "Apache, Version 2.0"

import re
import numpy as np
import ast

from vmaf import ExternalProgramCaller, to_list
from vmaf.config import VmafConfig, VmafExternalConfig
from vmaf.core.executor import Executor
from vmaf.core.result import Result
from vmaf.tools.reader import YuvReader

class FeatureExtractor(Executor):
    """
    FeatureExtractor takes in a list of assets, and run feature extraction on
    them, and return a list of corresponding results. A FeatureExtractor must
    specify a unique type and version combination (by the TYPE and VERSION
    attribute), so that the Result generated by it can be identified.

    A derived class of FeatureExtractor must:
        1) Override TYPE and VERSION
        2) Override _generate_result(self, asset), which call a
        command-line executable and generate feature scores in a log file.
        3) Override _get_feature_scores(self, asset), which read the feature
        scores from the log file, and return the scores in a dictionary format.
    For an example, follow VmafFeatureExtractor.
    """

    __metaclass__ = ABCMeta

    @property
    @abstractmethod
    def ATOM_FEATURES(self):
        raise NotImplementedError

    def _read_result(self, asset):
        result = {}
        result.update(self._get_feature_scores(asset))
        executor_id = self.executor_id
        return Result(asset, executor_id, result)

    @classmethod
    def get_scores_key(cls, atom_feature):
        return "{type}_{atom_feature}_scores".format(
            type=cls.TYPE, atom_feature=atom_feature)

    @classmethod
    def get_score_key(cls, atom_feature):
        return "{type}_{atom_feature}_score".format(
            type=cls.TYPE, atom_feature=atom_feature)

    def _get_feature_scores(self, asset):
        # routine to read the feature scores from the log file, and return
        # the scores in a dictionary format.

        log_file_path = self._get_log_file_path(asset)

        atom_feature_scores_dict = {}
        atom_feature_idx_dict = {}
        for atom_feature in self.ATOM_FEATURES:
            atom_feature_scores_dict[atom_feature] = []
            atom_feature_idx_dict[atom_feature] = 0

        with open(log_file_path, 'rt') as log_file:
            for line in log_file.readlines():
                for atom_feature in self.ATOM_FEATURES:
                    re_template = "{af}: ([0-9]+) ([a-zA-Z0-9.-]+)".format(af=atom_feature)
                    mo = re.match(re_template, line)
                    if mo:

                        cur_idx = int(mo.group(1))
                        assert cur_idx == atom_feature_idx_dict[atom_feature]

                        # parse value, allowing NaN and inf
                        val = float(mo.group(2))
                        if np.isnan(val) or np.isinf(val):
                            val = None

                        atom_feature_scores_dict[atom_feature].append(val)
                        atom_feature_idx_dict[atom_feature] += 1
                        continue

        len_score = len(atom_feature_scores_dict[self.ATOM_FEATURES[0]])
        assert len_score != 0
        for atom_feature in self.ATOM_FEATURES[1:]:
            assert len_score == len(atom_feature_scores_dict[atom_feature]), \
                "Feature data possibly corrupt. Run cleanup script and try again."

        feature_result = {}

        for atom_feature in self.ATOM_FEATURES:
            scores_key = self.get_scores_key(atom_feature)
            feature_result[scores_key] = atom_feature_scores_dict[atom_feature]

        return feature_result


class VmafFeatureExtractor(FeatureExtractor):

    TYPE = "VMAF_feature"

    # VERSION = '0.1' # vmaf_study; Anush's VIF fix
    # VERSION = '0.2' # expose vif_num, vif_den, adm_num, adm_den, anpsnr
    # VERSION = '0.2.1' # expose vif num/den of each scale
    # VERSION = '0.2.2'  # adm abs-->fabs, corrected border handling, uniform reading with option of offset for input YUV, updated VIF corner case
    # VERSION = '0.2.2b'  # expose adm_den/num_scalex
    # VERSION = '0.2.3'  # AVX for VMAF convolution; update adm features by folding noise floor into per coef
    # VERSION = '0.2.4'  # Fix a bug in adm feature passing scale into dwt_quant_step
    # VERSION = '0.2.4b'  # Modify by adding ADM noise floor outside cube root; add derived feature motion2
    VERSION = '0.2.4c'  # Modify by moving motion2 to c code

    ATOM_FEATURES = ['vif', 'adm', 'ansnr', 'motion', 'motion2',
                     'vif_num', 'vif_den', 'adm_num', 'adm_den', 'anpsnr',
                     'vif_num_scale0', 'vif_den_scale0',
                     'vif_num_scale1', 'vif_den_scale1',
                     'vif_num_scale2', 'vif_den_scale2',
                     'vif_num_scale3', 'vif_den_scale3',
                     'adm_num_scale0', 'adm_den_scale0',
                     'adm_num_scale1', 'adm_den_scale1',
                     'adm_num_scale2', 'adm_den_scale2',
                     'adm_num_scale3', 'adm_den_scale3',
                     ]

    DERIVED_ATOM_FEATURES = ['vif_scale0', 'vif_scale1', 'vif_scale2', 'vif_scale3',
                             'vif2', 'adm2', 'adm3',
                             'adm_scale0', 'adm_scale1', 'adm_scale2', 'adm_scale3',
                             ]

    ADM2_CONSTANT = 0
    ADM_SCALE_CONSTANT = 0

    def _generate_result(self, asset):
        # routine to call the command-line executable and generate feature
        # scores in the log file.

        quality_width, quality_height = asset.quality_width_height
        log_file_path = self._get_log_file_path(asset)

        yuv_type=self._get_workfile_yuv_type(asset)
        ref_path=asset.ref_workfile_path
        dis_path=asset.dis_workfile_path
        w=quality_width
        h=quality_height
        logger = self.logger

        ExternalProgramCaller.call_vmaf_feature(yuv_type, ref_path, dis_path, w, h, log_file_path, logger)

    @classmethod
    def _post_process_result(cls, result):
        # override Executor._post_process_result

        result = super(VmafFeatureExtractor, cls)._post_process_result(result)

        # adm2 =
        # (adm_num + ADM2_CONSTANT) / (adm_den + ADM2_CONSTANT)
        adm2_scores_key = cls.get_scores_key('adm2')
        adm_num_scores_key = cls.get_scores_key('adm_num')
        adm_den_scores_key = cls.get_scores_key('adm_den')
        result.result_dict[adm2_scores_key] = list(
            (np.array(result.result_dict[adm_num_scores_key]) + cls.ADM2_CONSTANT) /
            (np.array(result.result_dict[adm_den_scores_key]) + cls.ADM2_CONSTANT)
        )

        # vif_scalei = vif_num_scalei / vif_den_scalei, i = 0, 1, 2, 3
        vif_num_scale0_scores_key = cls.get_scores_key('vif_num_scale0')
        vif_den_scale0_scores_key = cls.get_scores_key('vif_den_scale0')
        vif_num_scale1_scores_key = cls.get_scores_key('vif_num_scale1')
        vif_den_scale1_scores_key = cls.get_scores_key('vif_den_scale1')
        vif_num_scale2_scores_key = cls.get_scores_key('vif_num_scale2')
        vif_den_scale2_scores_key = cls.get_scores_key('vif_den_scale2')
        vif_num_scale3_scores_key = cls.get_scores_key('vif_num_scale3')
        vif_den_scale3_scores_key = cls.get_scores_key('vif_den_scale3')
        vif_scale0_scores_key = cls.get_scores_key('vif_scale0')
        vif_scale1_scores_key = cls.get_scores_key('vif_scale1')
        vif_scale2_scores_key = cls.get_scores_key('vif_scale2')
        vif_scale3_scores_key = cls.get_scores_key('vif_scale3')
        result.result_dict[vif_scale0_scores_key] = list(
            (np.array(result.result_dict[vif_num_scale0_scores_key])
             / np.array(result.result_dict[vif_den_scale0_scores_key]))
        )
        result.result_dict[vif_scale1_scores_key] = list(
            (np.array(result.result_dict[vif_num_scale1_scores_key])
             / np.array(result.result_dict[vif_den_scale1_scores_key]))
        )
        result.result_dict[vif_scale2_scores_key] = list(
            (np.array(result.result_dict[vif_num_scale2_scores_key])
             / np.array(result.result_dict[vif_den_scale2_scores_key]))
        )
        result.result_dict[vif_scale3_scores_key] = list(
            (np.array(result.result_dict[vif_num_scale3_scores_key])
             / np.array(result.result_dict[vif_den_scale3_scores_key]))
        )

        # vif2 =
        # ((vif_num_scale0 / vif_den_scale0) + (vif_num_scale1 / vif_den_scale1) +
        # (vif_num_scale2 / vif_den_scale2) + (vif_num_scale3 / vif_den_scale3)) / 4.0
        vif_scores_key = cls.get_scores_key('vif2')
        result.result_dict[vif_scores_key] = list(
            (
                (np.array(result.result_dict[vif_num_scale0_scores_key])
                 / np.array(result.result_dict[vif_den_scale0_scores_key])) +
                (np.array(result.result_dict[vif_num_scale1_scores_key])
                 / np.array(result.result_dict[vif_den_scale1_scores_key])) +
                (np.array(result.result_dict[vif_num_scale2_scores_key])
                 / np.array(result.result_dict[vif_den_scale2_scores_key])) +
                (np.array(result.result_dict[vif_num_scale3_scores_key])
                 / np.array(result.result_dict[vif_den_scale3_scores_key]))
            ) / 4.0
        )

        # adm_scalei = adm_num_scalei / adm_den_scalei, i = 0, 1, 2, 3
        adm_num_scale0_scores_key = cls.get_scores_key('adm_num_scale0')
        adm_den_scale0_scores_key = cls.get_scores_key('adm_den_scale0')
        adm_num_scale1_scores_key = cls.get_scores_key('adm_num_scale1')
        adm_den_scale1_scores_key = cls.get_scores_key('adm_den_scale1')
        adm_num_scale2_scores_key = cls.get_scores_key('adm_num_scale2')
        adm_den_scale2_scores_key = cls.get_scores_key('adm_den_scale2')
        adm_num_scale3_scores_key = cls.get_scores_key('adm_num_scale3')
        adm_den_scale3_scores_key = cls.get_scores_key('adm_den_scale3')
        adm_scale0_scores_key = cls.get_scores_key('adm_scale0')
        adm_scale1_scores_key = cls.get_scores_key('adm_scale1')
        adm_scale2_scores_key = cls.get_scores_key('adm_scale2')
        adm_scale3_scores_key = cls.get_scores_key('adm_scale3')
        result.result_dict[adm_scale0_scores_key] = list(
            (np.array(result.result_dict[adm_num_scale0_scores_key]) + cls.ADM_SCALE_CONSTANT)
            / (np.array(result.result_dict[adm_den_scale0_scores_key]) + cls.ADM_SCALE_CONSTANT)
        )
        result.result_dict[adm_scale1_scores_key] = list(
            (np.array(result.result_dict[adm_num_scale1_scores_key]) + cls.ADM_SCALE_CONSTANT)
            / (np.array(result.result_dict[adm_den_scale1_scores_key]) + cls.ADM_SCALE_CONSTANT)
        )
        result.result_dict[adm_scale2_scores_key] = list(
            (np.array(result.result_dict[adm_num_scale2_scores_key]) + cls.ADM_SCALE_CONSTANT)
            / (np.array(result.result_dict[adm_den_scale2_scores_key]) + cls.ADM_SCALE_CONSTANT)
        )
        result.result_dict[adm_scale3_scores_key] = list(
            (np.array(result.result_dict[adm_num_scale3_scores_key]) + cls.ADM_SCALE_CONSTANT)
            / (np.array(result.result_dict[adm_den_scale3_scores_key]) + cls.ADM_SCALE_CONSTANT)
        )

        # adm3 = \
        # (((adm_num_scale0 + ADM_SCALE_CONSTANT) / (adm_den_scale0 + ADM_SCALE_CONSTANT))
        #  + ((adm_num_scale1 + ADM_SCALE_CONSTANT) / (adm_den_scale1 + ADM_SCALE_CONSTANT))
        #  + ((adm_num_scale2 + ADM_SCALE_CONSTANT) / (adm_den_scale2 + ADM_SCALE_CONSTANT))
        #  + ((adm_num_scale3 + ADM_SCALE_CONSTANT) / (adm_den_scale3 + ADM_SCALE_CONSTANT))) / 4.0
        adm3_scores_key = cls.get_scores_key('adm3')
        result.result_dict[adm3_scores_key] = list(
            (
                ((np.array(result.result_dict[adm_num_scale0_scores_key]) + cls.ADM_SCALE_CONSTANT)
                 / (np.array(result.result_dict[adm_den_scale0_scores_key]) + cls.ADM_SCALE_CONSTANT)) +
                ((np.array(result.result_dict[adm_num_scale1_scores_key]) + cls.ADM_SCALE_CONSTANT)
                 / (np.array(result.result_dict[adm_den_scale1_scores_key]) + cls.ADM_SCALE_CONSTANT)) +
                ((np.array(result.result_dict[adm_num_scale2_scores_key]) + cls.ADM_SCALE_CONSTANT)
                 / (np.array(result.result_dict[adm_den_scale2_scores_key]) + cls.ADM_SCALE_CONSTANT)) +
                ((np.array(result.result_dict[adm_num_scale3_scores_key]) + cls.ADM_SCALE_CONSTANT)
                 / (np.array(result.result_dict[adm_den_scale3_scores_key]) + cls.ADM_SCALE_CONSTANT))
            ) / 4.0
        )

        # validate
        for feature in cls.DERIVED_ATOM_FEATURES:
            assert cls.get_scores_key(feature) in result.result_dict

        return result

class VifFrameDifferenceFeatureExtractor(FeatureExtractor):

    TYPE = "VifDiff_feature"

    VERSION = '0.1'

    ATOM_FEATURES = ['vifdiff',
                     'vifdiff_num', 'vifdiff_den',
                     'vifdiff_num_scale0', 'vifdiff_den_scale0',
                     'vifdiff_num_scale1', 'vifdiff_den_scale1',
                     'vifdiff_num_scale2', 'vifdiff_den_scale2',
                     'vifdiff_num_scale3', 'vifdiff_den_scale3',
                     ]

    DERIVED_ATOM_FEATURES = ['vifdiff_scale0', 'vifdiff_scale1', 'vifdiff_scale2', 'vifdiff_scale3',
                             ]

    ADM2_CONSTANT = 0
    ADM_SCALE_CONSTANT = 0

    def _generate_result(self, asset):
        # routine to call the command-line executable and generate feature
        # scores in the log file.

        quality_width, quality_height = asset.quality_width_height
        log_file_path = self._get_log_file_path(asset)

        yuv_type=self._get_workfile_yuv_type(asset)
        ref_path=asset.ref_workfile_path
        dis_path=asset.dis_workfile_path
        w=quality_width
        h=quality_height
        logger = self.logger

        ExternalProgramCaller.call_vifdiff_feature(yuv_type, ref_path, dis_path, w, h, log_file_path, logger)

    @classmethod
    def _post_process_result(cls, result):
        # override Executor._post_process_result

        result = super(VifFrameDifferenceFeatureExtractor, cls)._post_process_result(result)

        # vifdiff_scalei = vifdiff_num_scalei / vifdiff_den_scalei, i = 0, 1, 2, 3
        vifdiff_num_scale0_scores_key = cls.get_scores_key('vifdiff_num_scale0')
        vifdiff_den_scale0_scores_key = cls.get_scores_key('vifdiff_den_scale0')
        vifdiff_num_scale1_scores_key = cls.get_scores_key('vifdiff_num_scale1')
        vifdiff_den_scale1_scores_key = cls.get_scores_key('vifdiff_den_scale1')
        vifdiff_num_scale2_scores_key = cls.get_scores_key('vifdiff_num_scale2')
        vifdiff_den_scale2_scores_key = cls.get_scores_key('vifdiff_den_scale2')
        vifdiff_num_scale3_scores_key = cls.get_scores_key('vifdiff_num_scale3')
        vifdiff_den_scale3_scores_key = cls.get_scores_key('vifdiff_den_scale3')
        vifdiff_scale0_scores_key = cls.get_scores_key('vifdiff_scale0')
        vifdiff_scale1_scores_key = cls.get_scores_key('vifdiff_scale1')
        vifdiff_scale2_scores_key = cls.get_scores_key('vifdiff_scale2')
        vifdiff_scale3_scores_key = cls.get_scores_key('vifdiff_scale3')
        result.result_dict[vifdiff_scale0_scores_key] = list(
            (np.array(result.result_dict[vifdiff_num_scale0_scores_key])
             / np.array(result.result_dict[vifdiff_den_scale0_scores_key]))
        )
        result.result_dict[vifdiff_scale1_scores_key] = list(
            (np.array(result.result_dict[vifdiff_num_scale1_scores_key])
             / np.array(result.result_dict[vifdiff_den_scale1_scores_key]))
        )
        result.result_dict[vifdiff_scale2_scores_key] = list(
            (np.array(result.result_dict[vifdiff_num_scale2_scores_key])
             / np.array(result.result_dict[vifdiff_den_scale2_scores_key]))
        )
        result.result_dict[vifdiff_scale3_scores_key] = list(
            (np.array(result.result_dict[vifdiff_num_scale3_scores_key])
             / np.array(result.result_dict[vifdiff_den_scale3_scores_key]))
        )

        # validate
        for feature in cls.DERIVED_ATOM_FEATURES:
            assert cls.get_scores_key(feature) in result.result_dict

        return result

class PsnrFeatureExtractor(FeatureExtractor):

    TYPE = "PSNR_feature"
    VERSION = "1.0"

    ATOM_FEATURES = ['psnr']

    def _generate_result(self, asset):
        # routine to call the command-line executable and generate quality
        # scores in the log file.

        quality_width, quality_height = asset.quality_width_height
        log_file_path = self._get_log_file_path(asset)

        yuv_type=self._get_workfile_yuv_type(asset)
        ref_path=asset.ref_workfile_path
        dis_path=asset.dis_workfile_path
        w=quality_width
        h=quality_height
        logger = self.logger

        ExternalProgramCaller.call_psnr(yuv_type, ref_path, dis_path, w, h, log_file_path, logger)


class MomentFeatureExtractor(FeatureExtractor):

    TYPE = "Moment_feature"

    # VERSION = "1.0" # call executable
    VERSION = "1.1" # python only

    ATOM_FEATURES = ['ref1st', 'ref2nd', 'dis1st', 'dis2nd', ]

    DERIVED_ATOM_FEATURES = ['refvar', 'disvar', ]

    def _generate_result(self, asset):
        # routine to call the command-line executable and generate feature
        # scores in the log file.

        quality_w, quality_h = asset.quality_width_height

        ref_scores_mtx = None
        with YuvReader(filepath=asset.ref_workfile_path, width=quality_w, height=quality_h,
                       yuv_type=self._get_workfile_yuv_type(asset)) as ref_yuv_reader:
            scores_mtx_list = []
            i = 0
            for ref_yuv in ref_yuv_reader:
                ref_y = ref_yuv[0]
                firstm = ref_y.mean()
                secondm = ref_y.var() + firstm**2
                scores_mtx_list.append(np.hstack(([firstm], [secondm])))
                i += 1
            ref_scores_mtx = np.vstack(scores_mtx_list)

        dis_scores_mtx = None
        with YuvReader(filepath=asset.dis_workfile_path, width=quality_w, height=quality_h,
                       yuv_type=self._get_workfile_yuv_type(asset)) as dis_yuv_reader:
            scores_mtx_list = []
            i = 0
            for dis_yuv in dis_yuv_reader:
                dis_y = dis_yuv[0]
                firstm = dis_y.mean()
                secondm = dis_y.var() + firstm**2
                scores_mtx_list.append(np.hstack(([firstm], [secondm])))
                i += 1
            dis_scores_mtx = np.vstack(scores_mtx_list)

        assert ref_scores_mtx is not None and dis_scores_mtx is not None

        log_dict = {'ref_scores_mtx': ref_scores_mtx.tolist(),
                    'dis_scores_mtx': dis_scores_mtx.tolist()}

        log_file_path = self._get_log_file_path(asset)
        with open(log_file_path, 'wt') as log_file:
            log_file.write(str(log_dict))

    def _get_feature_scores(self, asset):
        # routine to read the feature scores from the log file, and return
        # the scores in a dictionary format.

        log_file_path = self._get_log_file_path(asset)

        with open(log_file_path, 'rt') as log_file:
            log_str = log_file.read()
            log_dict = ast.literal_eval(log_str)
        ref_scores_mtx = np.array(log_dict['ref_scores_mtx'])
        dis_scores_mtx = np.array(log_dict['dis_scores_mtx'])

        _, num_ref_features = ref_scores_mtx.shape
        assert num_ref_features == 2 # ref1st, ref2nd
        _, num_dis_features = dis_scores_mtx.shape
        assert num_dis_features == 2 # dis1st, dis2nd

        feature_result = {}
        feature_result[self.get_scores_key('ref1st')] = list(ref_scores_mtx[:, 0])
        feature_result[self.get_scores_key('ref2nd')] = list(ref_scores_mtx[:, 1])
        feature_result[self.get_scores_key('dis1st')] = list(dis_scores_mtx[:, 0])
        feature_result[self.get_scores_key('dis2nd')] = list(dis_scores_mtx[:, 1])

        return feature_result

    @classmethod
    def _post_process_result(cls, result):
        # override Executor._post_process_result

        result = super(MomentFeatureExtractor, cls)._post_process_result(result)

        # calculate refvar and disvar from ref1st, ref2nd, dis1st, dis2nd
        refvar_scores_key = cls.get_scores_key('refvar')
        ref1st_scores_key = cls.get_scores_key('ref1st')
        ref2nd_scores_key = cls.get_scores_key('ref2nd')
        disvar_scores_key = cls.get_scores_key('disvar')
        dis1st_scores_key = cls.get_scores_key('dis1st')
        dis2nd_scores_key = cls.get_scores_key('dis2nd')
        get_var = lambda m: m[1] - m[0] * m[0]
        result.result_dict[refvar_scores_key] = \
            to_list(map(get_var, zip(result.result_dict[ref1st_scores_key],
                             result.result_dict[ref2nd_scores_key])))
        result.result_dict[disvar_scores_key] = \
            to_list(map(get_var, zip(result.result_dict[dis1st_scores_key],
                             result.result_dict[dis2nd_scores_key])))

        # validate
        for feature in cls.DERIVED_ATOM_FEATURES:
            assert cls.get_scores_key(feature) in result.result_dict

        return result

class SsimFeatureExtractor(FeatureExtractor):

    TYPE = "SSIM_feature"
    # VERSION = "1.0"
    VERSION = "1.1" # fix OPT_RANGE_PIXEL_OFFSET = 0

    ATOM_FEATURES = ['ssim', 'ssim_l', 'ssim_c', 'ssim_s']

    def _generate_result(self, asset):
        # routine to call the command-line executable and generate quality
        # scores in the log file.

        quality_width, quality_height = asset.quality_width_height
        log_file_path = self._get_log_file_path(asset)

        yuv_type=self._get_workfile_yuv_type(asset)
        ref_path=asset.ref_workfile_path
        dis_path=asset.dis_workfile_path
        w=quality_width
        h=quality_height
        logger = self.logger

        ExternalProgramCaller.call_ssim(yuv_type, ref_path, dis_path, w, h, log_file_path, logger)


class MsSsimFeatureExtractor(FeatureExtractor):

    TYPE = "MS_SSIM_feature"
    # VERSION = "1.0"
    VERSION = "1.1" # fix OPT_RANGE_PIXEL_OFFSET = 0

    ATOM_FEATURES = ['ms_ssim',
                     'ms_ssim_l_scale0', 'ms_ssim_c_scale0', 'ms_ssim_s_scale0',
                     'ms_ssim_l_scale1', 'ms_ssim_c_scale1', 'ms_ssim_s_scale1',
                     'ms_ssim_l_scale2', 'ms_ssim_c_scale2', 'ms_ssim_s_scale2',
                     'ms_ssim_l_scale3', 'ms_ssim_c_scale3', 'ms_ssim_s_scale3',
                     'ms_ssim_l_scale4', 'ms_ssim_c_scale4', 'ms_ssim_s_scale4',
                     ]

    def _generate_result(self, asset):
        # routine to call the command-line executable and generate quality
        # scores in the log file.

        quality_width, quality_height = asset.quality_width_height
        log_file_path = self._get_log_file_path(asset)

        yuv_type=self._get_workfile_yuv_type(asset)
        ref_path=asset.ref_workfile_path
        dis_path=asset.dis_workfile_path
        w=quality_width
        h=quality_height
        logger = self.logger

        ExternalProgramCaller.call_ms_ssim(yuv_type, ref_path, dis_path, w, h, log_file_path, logger)



[((46, 15, 46, 49), 'vmaf.core.result.Result', 'Result', ({(46, 22, 46, 27): 'asset', (46, 29, 46, 40): 'executor_id', (46, 42, 46, 48): 'result'}, {}), '(asset, executor_id, result)', False, 'from vmaf.core.result import Result\n'), ((152, 8, 152, 106), 'vmaf.ExternalProgramCaller.call_vmaf_feature', 'ExternalProgramCaller.call_vmaf_feature', ({(152, 48, 152, 56): 'yuv_type', (152, 58, 152, 66): 'ref_path', (152, 68, 152, 76): 'dis_path', (152, 78, 152, 79): 'w', (152, 81, 152, 82): 'h', (152, 84, 152, 97): 'log_file_path', (152, 99, 152, 105): 'logger'}, {}), '(yuv_type, ref_path, dis_path, w, h,\n    log_file_path, logger)', False, 'from vmaf import ExternalProgramCaller, to_list\n'), ((306, 8, 306, 109), 'vmaf.ExternalProgramCaller.call_vifdiff_feature', 'ExternalProgramCaller.call_vifdiff_feature', ({(306, 51, 306, 59): 'yuv_type', (306, 61, 306, 69): 'ref_path', (306, 71, 306, 79): 'dis_path', (306, 81, 306, 82): 'w', (306, 84, 306, 85): 'h', (306, 87, 306, 100): 'log_file_path', (306, 102, 306, 108): 'logger'}, {}), '(yuv_type, ref_path, dis_path, w,\n    h, log_file_path, logger)', False, 'from vmaf import ExternalProgramCaller, to_list\n'), ((371, 8, 371, 98), 'vmaf.ExternalProgramCaller.call_psnr', 'ExternalProgramCaller.call_psnr', ({(371, 40, 371, 48): 'yuv_type', (371, 50, 371, 58): 'ref_path', (371, 60, 371, 68): 'dis_path', (371, 70, 371, 71): 'w', (371, 73, 371, 74): 'h', (371, 76, 371, 89): 'log_file_path', (371, 91, 371, 97): 'logger'}, {}), '(yuv_type, ref_path, dis_path, w, h,\n    log_file_path, logger)', False, 'from vmaf import ExternalProgramCaller, to_list\n'), ((435, 25, 435, 61), 'numpy.array', 'np.array', ({(435, 34, 435, 60): "log_dict['ref_scores_mtx']"}, {}), "(log_dict['ref_scores_mtx'])", True, 'import numpy as np\n'), ((436, 25, 436, 61), 'numpy.array', 'np.array', ({(436, 34, 436, 60): "log_dict['dis_scores_mtx']"}, {}), "(log_dict['dis_scores_mtx'])", True, 'import numpy as np\n'), ((500, 8, 500, 98), 'vmaf.ExternalProgramCaller.call_ssim', 'ExternalProgramCaller.call_ssim', ({(500, 40, 500, 48): 'yuv_type', (500, 50, 500, 58): 'ref_path', (500, 60, 500, 68): 'dis_path', (500, 70, 500, 71): 'w', (500, 73, 500, 74): 'h', (500, 76, 500, 89): 'log_file_path', (500, 91, 500, 97): 'logger'}, {}), '(yuv_type, ref_path, dis_path, w, h,\n    log_file_path, logger)', False, 'from vmaf import ExternalProgramCaller, to_list\n'), ((531, 8, 531, 101), 'vmaf.ExternalProgramCaller.call_ms_ssim', 'ExternalProgramCaller.call_ms_ssim', ({(531, 43, 531, 51): 'yuv_type', (531, 53, 531, 61): 'ref_path', (531, 63, 531, 71): 'dis_path', (531, 73, 531, 74): 'w', (531, 76, 531, 77): 'h', (531, 79, 531, 92): 'log_file_path', (531, 94, 531, 100): 'logger'}, {}), '(yuv_type, ref_path, dis_path, w, h,\n    log_file_path, logger)', False, 'from vmaf import ExternalProgramCaller, to_list\n'), ((402, 29, 402, 55), 'numpy.vstack', 'np.vstack', ({(402, 39, 402, 54): 'scores_mtx_list'}, {}), '(scores_mtx_list)', True, 'import numpy as np\n'), ((415, 29, 415, 55), 'numpy.vstack', 'np.vstack', ({(415, 39, 415, 54): 'scores_mtx_list'}, {}), '(scores_mtx_list)', True, 'import numpy as np\n'), ((434, 23, 434, 48), 'ast.literal_eval', 'ast.literal_eval', ({(434, 40, 434, 47): 'log_str'}, {}), '(log_str)', False, 'import ast\n'), ((184, 13, 184, 68), 'numpy.array', 'np.array', ({(184, 22, 184, 67): 'result.result_dict[vif_num_scale0_scores_key]'}, {}), '(result.result_dict[vif_num_scale0_scores_key])', True, 'import numpy as np\n'), ((185, 15, 185, 70), 'numpy.array', 'np.array', ({(185, 24, 185, 69): 'result.result_dict[vif_den_scale0_scores_key]'}, {}), '(result.result_dict[vif_den_scale0_scores_key])', True, 'import numpy as np\n'), ((188, 13, 188, 68), 'numpy.array', 'np.array', ({(188, 22, 188, 67): 'result.result_dict[vif_num_scale1_scores_key]'}, {}), '(result.result_dict[vif_num_scale1_scores_key])', True, 'import numpy as np\n'), ((189, 15, 189, 70), 'numpy.array', 'np.array', ({(189, 24, 189, 69): 'result.result_dict[vif_den_scale1_scores_key]'}, {}), '(result.result_dict[vif_den_scale1_scores_key])', True, 'import numpy as np\n'), ((192, 13, 192, 68), 'numpy.array', 'np.array', ({(192, 22, 192, 67): 'result.result_dict[vif_num_scale2_scores_key]'}, {}), '(result.result_dict[vif_num_scale2_scores_key])', True, 'import numpy as np\n'), ((193, 15, 193, 70), 'numpy.array', 'np.array', ({(193, 24, 193, 69): 'result.result_dict[vif_den_scale2_scores_key]'}, {}), '(result.result_dict[vif_den_scale2_scores_key])', True, 'import numpy as np\n'), ((196, 13, 196, 68), 'numpy.array', 'np.array', ({(196, 22, 196, 67): 'result.result_dict[vif_num_scale3_scores_key]'}, {}), '(result.result_dict[vif_num_scale3_scores_key])', True, 'import numpy as np\n'), ((197, 15, 197, 70), 'numpy.array', 'np.array', ({(197, 24, 197, 69): 'result.result_dict[vif_den_scale3_scores_key]'}, {}), '(result.result_dict[vif_den_scale3_scores_key])', True, 'import numpy as np\n'), ((328, 13, 328, 72), 'numpy.array', 'np.array', ({(328, 22, 328, 71): 'result.result_dict[vifdiff_num_scale0_scores_key]'}, {}), '(result.result_dict[vifdiff_num_scale0_scores_key])', True, 'import numpy as np\n'), ((329, 15, 329, 74), 'numpy.array', 'np.array', ({(329, 24, 329, 73): 'result.result_dict[vifdiff_den_scale0_scores_key]'}, {}), '(result.result_dict[vifdiff_den_scale0_scores_key])', True, 'import numpy as np\n'), ((332, 13, 332, 72), 'numpy.array', 'np.array', ({(332, 22, 332, 71): 'result.result_dict[vifdiff_num_scale1_scores_key]'}, {}), '(result.result_dict[vifdiff_num_scale1_scores_key])', True, 'import numpy as np\n'), ((333, 15, 333, 74), 'numpy.array', 'np.array', ({(333, 24, 333, 73): 'result.result_dict[vifdiff_den_scale1_scores_key]'}, {}), '(result.result_dict[vifdiff_den_scale1_scores_key])', True, 'import numpy as np\n'), ((336, 13, 336, 72), 'numpy.array', 'np.array', ({(336, 22, 336, 71): 'result.result_dict[vifdiff_num_scale2_scores_key]'}, {}), '(result.result_dict[vifdiff_num_scale2_scores_key])', True, 'import numpy as np\n'), ((337, 15, 337, 74), 'numpy.array', 'np.array', ({(337, 24, 337, 73): 'result.result_dict[vifdiff_den_scale2_scores_key]'}, {}), '(result.result_dict[vifdiff_den_scale2_scores_key])', True, 'import numpy as np\n'), ((340, 13, 340, 72), 'numpy.array', 'np.array', ({(340, 22, 340, 71): 'result.result_dict[vifdiff_num_scale3_scores_key]'}, {}), '(result.result_dict[vifdiff_num_scale3_scores_key])', True, 'import numpy as np\n'), ((341, 15, 341, 74), 'numpy.array', 'np.array', ({(341, 24, 341, 73): 'result.result_dict[vifdiff_den_scale3_scores_key]'}, {}), '(result.result_dict[vifdiff_den_scale3_scores_key])', True, 'import numpy as np\n'), ((74, 25, 74, 52), 're.match', 're.match', ({(74, 34, 74, 45): 're_template', (74, 47, 74, 51): 'line'}, {}), '(re_template, line)', False, 'import re\n'), ((166, 13, 166, 61), 'numpy.array', 'np.array', ({(166, 22, 166, 60): 'result.result_dict[adm_num_scores_key]'}, {}), '(result.result_dict[adm_num_scores_key])', True, 'import numpy as np\n'), ((167, 13, 167, 61), 'numpy.array', 'np.array', ({(167, 22, 167, 60): 'result.result_dict[adm_den_scores_key]'}, {}), '(result.result_dict[adm_den_scores_key])', True, 'import numpy as np\n'), ((231, 13, 231, 68), 'numpy.array', 'np.array', ({(231, 22, 231, 67): 'result.result_dict[adm_num_scale0_scores_key]'}, {}), '(result.result_dict[adm_num_scale0_scores_key])', True, 'import numpy as np\n'), ((232, 15, 232, 70), 'numpy.array', 'np.array', ({(232, 24, 232, 69): 'result.result_dict[adm_den_scale0_scores_key]'}, {}), '(result.result_dict[adm_den_scale0_scores_key])', True, 'import numpy as np\n'), ((235, 13, 235, 68), 'numpy.array', 'np.array', ({(235, 22, 235, 67): 'result.result_dict[adm_num_scale1_scores_key]'}, {}), '(result.result_dict[adm_num_scale1_scores_key])', True, 'import numpy as np\n'), ((236, 15, 236, 70), 'numpy.array', 'np.array', ({(236, 24, 236, 69): 'result.result_dict[adm_den_scale1_scores_key]'}, {}), '(result.result_dict[adm_den_scale1_scores_key])', True, 'import numpy as np\n'), ((239, 13, 239, 68), 'numpy.array', 'np.array', ({(239, 22, 239, 67): 'result.result_dict[adm_num_scale2_scores_key]'}, {}), '(result.result_dict[adm_num_scale2_scores_key])', True, 'import numpy as np\n'), ((240, 15, 240, 70), 'numpy.array', 'np.array', ({(240, 24, 240, 69): 'result.result_dict[adm_den_scale2_scores_key]'}, {}), '(result.result_dict[adm_den_scale2_scores_key])', True, 'import numpy as np\n'), ((243, 13, 243, 68), 'numpy.array', 'np.array', ({(243, 22, 243, 67): 'result.result_dict[adm_num_scale3_scores_key]'}, {}), '(result.result_dict[adm_num_scale3_scores_key])', True, 'import numpy as np\n'), ((244, 15, 244, 70), 'numpy.array', 'np.array', ({(244, 24, 244, 69): 'result.result_dict[adm_den_scale3_scores_key]'}, {}), '(result.result_dict[adm_den_scale3_scores_key])', True, 'import numpy as np\n'), ((400, 39, 400, 71), 'numpy.hstack', 'np.hstack', ({(400, 49, 400, 70): '([firstm], [secondm])'}, {}), '(([firstm], [secondm]))', True, 'import numpy as np\n'), ((413, 39, 413, 71), 'numpy.hstack', 'np.hstack', ({(413, 49, 413, 70): '([firstm], [secondm])'}, {}), '(([firstm], [secondm]))', True, 'import numpy as np\n'), ((212, 17, 212, 72), 'numpy.array', 'np.array', ({(212, 26, 212, 71): 'result.result_dict[vif_num_scale3_scores_key]'}, {}), '(result.result_dict[vif_num_scale3_scores_key])', True, 'import numpy as np\n'), ((213, 19, 213, 74), 'numpy.array', 'np.array', ({(213, 28, 213, 73): 'result.result_dict[vif_den_scale3_scores_key]'}, {}), '(result.result_dict[vif_den_scale3_scores_key])', True, 'import numpy as np\n'), ((82, 27, 82, 40), 'numpy.isnan', 'np.isnan', ({(82, 36, 82, 39): 'val'}, {}), '(val)', True, 'import numpy as np\n'), ((82, 44, 82, 57), 'numpy.isinf', 'np.isinf', ({(82, 53, 82, 56): 'val'}, {}), '(val)', True, 'import numpy as np\n'), ((210, 17, 210, 72), 'numpy.array', 'np.array', ({(210, 26, 210, 71): 'result.result_dict[vif_num_scale2_scores_key]'}, {}), '(result.result_dict[vif_num_scale2_scores_key])', True, 'import numpy as np\n'), ((211, 19, 211, 74), 'numpy.array', 'np.array', ({(211, 28, 211, 73): 'result.result_dict[vif_den_scale2_scores_key]'}, {}), '(result.result_dict[vif_den_scale2_scores_key])', True, 'import numpy as np\n'), ((261, 18, 261, 73), 'numpy.array', 'np.array', ({(261, 27, 261, 72): 'result.result_dict[adm_num_scale3_scores_key]'}, {}), '(result.result_dict[adm_num_scale3_scores_key])', True, 'import numpy as np\n'), ((262, 20, 262, 75), 'numpy.array', 'np.array', ({(262, 29, 262, 74): 'result.result_dict[adm_den_scale3_scores_key]'}, {}), '(result.result_dict[adm_den_scale3_scores_key])', True, 'import numpy as np\n'), ((206, 17, 206, 72), 'numpy.array', 'np.array', ({(206, 26, 206, 71): 'result.result_dict[vif_num_scale0_scores_key]'}, {}), '(result.result_dict[vif_num_scale0_scores_key])', True, 'import numpy as np\n'), ((207, 19, 207, 74), 'numpy.array', 'np.array', ({(207, 28, 207, 73): 'result.result_dict[vif_den_scale0_scores_key]'}, {}), '(result.result_dict[vif_den_scale0_scores_key])', True, 'import numpy as np\n'), ((208, 17, 208, 72), 'numpy.array', 'np.array', ({(208, 26, 208, 71): 'result.result_dict[vif_num_scale1_scores_key]'}, {}), '(result.result_dict[vif_num_scale1_scores_key])', True, 'import numpy as np\n'), ((209, 19, 209, 74), 'numpy.array', 'np.array', ({(209, 28, 209, 73): 'result.result_dict[vif_den_scale1_scores_key]'}, {}), '(result.result_dict[vif_den_scale1_scores_key])', True, 'import numpy as np\n'), ((259, 18, 259, 73), 'numpy.array', 'np.array', ({(259, 27, 259, 72): 'result.result_dict[adm_num_scale2_scores_key]'}, {}), '(result.result_dict[adm_num_scale2_scores_key])', True, 'import numpy as np\n'), ((260, 20, 260, 75), 'numpy.array', 'np.array', ({(260, 29, 260, 74): 'result.result_dict[adm_den_scale2_scores_key]'}, {}), '(result.result_dict[adm_den_scale2_scores_key])', True, 'import numpy as np\n'), ((255, 18, 255, 73), 'numpy.array', 'np.array', ({(255, 27, 255, 72): 'result.result_dict[adm_num_scale0_scores_key]'}, {}), '(result.result_dict[adm_num_scale0_scores_key])', True, 'import numpy as np\n'), ((256, 20, 256, 75), 'numpy.array', 'np.array', ({(256, 29, 256, 74): 'result.result_dict[adm_den_scale0_scores_key]'}, {}), '(result.result_dict[adm_den_scale0_scores_key])', True, 'import numpy as np\n'), ((257, 18, 257, 73), 'numpy.array', 'np.array', ({(257, 27, 257, 72): 'result.result_dict[adm_num_scale1_scores_key]'}, {}), '(result.result_dict[adm_num_scale1_scores_key])', True, 'import numpy as np\n'), ((258, 20, 258, 75), 'numpy.array', 'np.array', ({(258, 29, 258, 74): 'result.result_dict[adm_den_scale1_scores_key]'}, {}), '(result.result_dict[adm_den_scale1_scores_key])', True, 'import numpy as np\n')]