def CombineImages(list_imgs_folder):
    
    #define new list
    list_imgs_bgr,list_imgs_gray,list_VCM_code=[],[],[]
    
    for this_imgs_folder in list_imgs_folder:
        
        this_list_imgs_bgr,\
        this_list_imgs_gray,\
        this_list_VCM_code=BatchImages(this_imgs_folder)
        
        #combine this list
        list_imgs_bgr+=this_list_imgs_bgr
        list_imgs_gray+=this_list_imgs_gray
        list_VCM_code+=this_list_VCM_code
        
    #construct map between VCM code and imgs_gray/imgs_bgr
    map_VCM_code_imgs_bgr=dict(zip(list_VCM_code,list_imgs_bgr))
    map_VCM_code_imgs_gray=dict(zip(list_VCM_code,list_imgs_gray))
    
    #sort the VCM code list in an ascending order
    list_VCM_code=list(set(list_VCM_code))
    list_VCM_code.sort()
    
    #sort imgs
    list_imgs_bgr=list(O_D.DictSortByIndex(map_VCM_code_imgs_bgr,list_VCM_code).values())
    list_imgs_gray=list(O_D.DictSortByIndex(map_VCM_code_imgs_gray,list_VCM_code).values())
    
    return list_imgs_bgr,list_imgs_gray,list_VCM_code
def FramesConstruction(imgs_folder,operator,ROI_mode):
    
    #final image object list and their paths
    list_frames=[]

    #traverse all image
    for this_img_name in os.listdir(imgs_folder):
        
        if '.jpg' not in this_img_name and '.png' not in this_img_name:

            continue
        
        #define a new image object
        that_frame=frame()

        that_frame.path=imgs_folder+'\\'+this_img_name
        that_frame.pre_fix=pre_fix
        
        that_frame.Init(operator,ROI_mode)
        
        list_frames.append(that_frame)
        
    list_VCM_code=[this_frame.lens_position_code for this_frame in list_frames]
    
    #construct map between VCM code and frame object
    map_VCM_code_frames=dict(zip(list_VCM_code,list_frames))
    
    #sort the VCM code list in an ascending order
    list_VCM_code=list(set(list_VCM_code))
    list_VCM_code.sort()
    
    return list(O_D.DictSortByIndex(map_VCM_code_frames,list_VCM_code).values())   
def BatchImages(imgs_folder):
    
    '''cv2 read image with the format of BGR meanwhile plt read it with the one of RGB'''
    #list to contain img matrix (rgb and gray)
    list_imgs_bgr=[]
    list_imgs_gray=[]
    list_VCM_code=[]
    
    #traverse all image
    for this_img_name in os.listdir(imgs_folder):

        if '.jpg' in this_img_name or '.png' in this_img_name:
            
            this_img_path=imgs_folder+'\\'+this_img_name
            
            #read image
            this_img_rgb=cv2.imread(this_img_path)
            
            #convert rgb img to gray img
            this_img_gray=cv2.cvtColor(this_img_rgb,cv2.COLOR_BGR2GRAY)
            
            #collect it
            list_imgs_bgr.append(this_img_rgb)
            list_imgs_gray.append(this_img_gray)
            
            if '.jpg' in this_img_name:
                
                list_VCM_code.append(int(this_img_name.strip('.jpg').split(pre_fix)[-1]))
            
            if '.png' in this_img_name:
                
                list_VCM_code.append(int(this_img_name.strip('.png').split(pre_fix)[-1]))
            
    #construct map between VCM code and imgs_gray/imgs_bgr
    map_VCM_code_imgs_bgr=dict(zip(list_VCM_code,list_imgs_bgr))
    map_VCM_code_imgs_gray=dict(zip(list_VCM_code,list_imgs_gray))
    
    #sort the VCM code list in an ascending order
    list_VCM_code.sort()

    #sort imgs
    list_imgs_bgr=list(O_D.DictSortByIndex(map_VCM_code_imgs_bgr,list_VCM_code).values())
    list_imgs_gray=list(O_D.DictSortByIndex(map_VCM_code_imgs_gray,list_VCM_code).values())
    
    return list_imgs_bgr,list_imgs_gray,list_VCM_code
def CombineFrames(list_imgs_folder):
    
    #total frames
    list_frames=[]
    
    for this_imgs_folder in list_imgs_folder:
        
        list_frames+=FramesConstruction(this_imgs_folder) 
        
    #total VCM code
    list_VCM_code=[this_frame.VCM_code for this_frame in list_frames]
    
    #construct map between VCM code and frame object
    map_VCM_code_frames=dict(zip(list_VCM_code,list_frames))
    
    #sort the VCM code list in an ascending order
    list_VCM_code=list(set(list_VCM_code))
    list_VCM_code.sort()
    
    return list(O_D.DictSortByIndex(map_VCM_code_frames,list_VCM_code).values())   
def FullSweep(imgs_folder, operator, ROI_mode):

    print('')
    print('-- Full Sweep')
    print('-> Operator:', operator)
    print('')

    str_a, str_b = imgs_folder.split('Material')
    str_c, str_d = imgs_folder.split('Material')[-1].strip('\\').split('\\')

    #construct output folder
    output_folder_operator = str_a + '\\Curve\\Scenario' + str_b

    try:

        output_folder_condition = output_folder_operator.split(
            str_c)[0].replace('\\Scenario', '') + '\\Operator'

    except:

        output_folder_condition = str_a + '\\Curve\Operator'

    output_folder_operator += '\\' + operator + '\\'
    output_folder_condition += '\\' + operator + '\\'

    O_P.GenerateFolder(output_folder_operator)
    O_P.GenerateFolder(output_folder_condition)
    '''need optimized frames construction'''
    list_imgs_folder = [
        imgs_folder + '\\' + this_name for this_name in os.listdir(imgs_folder)
        if '.' not in this_name
    ]

    #construct frame objects
    imgs_folder_coarse = ''
    imgs_folder_fine = ''

    for this_imgs_folder in list_imgs_folder:

        if 'Coarse' in this_imgs_folder:

            imgs_folder_coarse = cp.deepcopy(this_imgs_folder)

        if 'Fine' in this_imgs_folder:

            imgs_folder_fine = cp.deepcopy(this_imgs_folder)

    if imgs_folder_coarse == '' or imgs_folder_fine == '':

        print('=> WARNING: No both Corase and Fine Folder')

        imgs_folder_coarse = cp.deepcopy(imgs_folder)

    #frame object for coarse and fine search
    frames_coarse = O_I.FramesConstruction(imgs_folder_coarse)

    try:

        frames_fine = O_I.FramesConstruction(imgs_folder_fine)

    except:

        pass

    #total data of coarse frames
    list_VCM_code_coarse = []
    list_contrast_coarse = []
    list_img_ROI_coarse = []

    if operator not in list_operator:

        return print('=> ERROR: Incorrect Input')

    result_full_sweep = None

    print('')
    print('-- Full Sweep Coarse')

    #traverse all frames for full sweeping
    for this_frame in frames_coarse:

        img_gray = this_frame.img_gray

        #size of img
        height, width = np.shape(img_gray)

        #basic parameters
        ROI_weight = [0.44, 0.14, 0.14, 0.14, 0.14]
        zoom_factor = 16
        ROI_linewidth = int(height // 300)
        text_position = 'Contrast'

        #    print(height,width)

        #image of ROI
        this_img_ROI = np.full(np.shape(img_gray), np.nan)

        list_5_points = [[height / 2, width / 2], [height / 4, width / 4],
                         [height / 4, -width / 4], [-height / 4, -width / 4],
                         [-height / 4, width / 4]]

        #size of area
        area_half_height = int(np.shape(img_gray)[0] / zoom_factor)
        area_half_width = int(np.shape(img_gray)[1] / zoom_factor)

        list_VCM_code_coarse.append(this_frame.VCM_code)

        #calculate contrast in each area
        list_contrast_5_areas = []

        if ROI_mode == '5-Area':

            for i, j in list_5_points:

                this_area = img_gray[int(i) - area_half_height:int(i) +
                                     area_half_height,
                                     int(j) - area_half_width:int(j) +
                                     area_half_width]

                #collect it
                list_contrast_5_areas.append(
                    C_C.GlobalContrast(this_area, operator))

                #draw the bound of ROI
                for k in range(ROI_linewidth):

                    this_img_ROI[int(i - k) - area_half_height,
                                 int(j - k) - area_half_width:int(j + k + 1) +
                                 area_half_width] = 1
                    this_img_ROI[int(i + k) + area_half_height,
                                 int(j - k) - area_half_width:int(j + k + 1) +
                                 area_half_width] = 1
                    this_img_ROI[int(i - k) - area_half_height:int(i + k + 1) +
                                 area_half_height,
                                 int(j - k) - area_half_width] = 1
                    this_img_ROI[int(i - k) - area_half_height:int(i + k + 1) +
                                 area_half_height,
                                 int(j + k) + area_half_width] = 1

            #collect the data
            list_contrast_coarse.append(
                np.sum(np.array(ROI_weight) * np.array(list_contrast_5_areas)))
            list_img_ROI_coarse.append(this_img_ROI)

        if ROI_mode == 'Center':

            for i, j in list_5_points[:1]:

                this_area = img_gray[int(i) - area_half_height:int(i) +
                                     area_half_height,
                                     int(j) - area_half_width:int(j) +
                                     area_half_width]

                #collect it
                list_contrast_5_areas.append(
                    C_C.GlobalContrast(this_area, operator))

                #draw the bound of ROI
                for k in range(ROI_linewidth):

                    this_img_ROI[int(i - k) - area_half_height,
                                 int(j - k) - area_half_width:int(j + k + 1) +
                                 area_half_width] = 1
                    this_img_ROI[int(i + k) + area_half_height,
                                 int(j - k) - area_half_width:int(j + k + 1) +
                                 area_half_width] = 1
                    this_img_ROI[int(i - k) - area_half_height:int(i + k + 1) +
                                 area_half_height,
                                 int(j - k) - area_half_width] = 1
                    this_img_ROI[int(i - k) - area_half_height:int(i + k + 1) +
                                 area_half_height,
                                 int(j + k) + area_half_width] = 1

            #collect the data
            list_contrast_coarse.append(list_contrast_5_areas[0])
            list_img_ROI_coarse.append(this_img_ROI)

        #result of full sweep
        result_full_sweep = JLSearch(list_contrast_coarse)

        print('--> VCM Code:', this_frame.VCM_code)
        # print('---> Contrast:',list_contrast_coarse[-1])

        if result_full_sweep is not None:

            #index for fine search
            index_start, index_end = result_full_sweep

            break

    #no valid peak: select maximum instead
    if result_full_sweep is None:

        index_start = list_contrast_coarse.index(
            np.max(list_contrast_coarse)) - 1
        index_end = list_contrast_coarse.index(
            np.max(list_contrast_coarse)) + 1

    #expetion
    if index_start < 0:

        index_start = 0

    if index_end >= len(list_contrast_coarse):

        index_end = len(list_contrast_coarse) - 1

    #VCM code boundary for fine search
    VCM_code_start = list_VCM_code_coarse[index_start]
    VCM_code_end = list_VCM_code_coarse[index_end]

    #frame list for final search
    frames = []

    try:

        for this_frame in frames_fine:

            if VCM_code_start <= this_frame.VCM_code <= VCM_code_end:

                frames.append(this_frame)

    except:

        for this_frame in frames_coarse:

            if VCM_code_start <= this_frame.VCM_code <= VCM_code_end:

                frames.append(this_frame)

    #if blank
    if frames == []:

        flag = 'Coarse'

        list_VCM_code_fine = []
        list_contrast_fine = []

    else:

        flag = 'Fine'

        #total data of fine frames
        list_VCM_code_fine = []
        list_contrast_fine = []
        list_img_ROI_fine = []

        print('')
        print('-- Full Sweep Fine')

        #traverse all frames for full sweeping
        for this_frame in frames:

            img_gray = this_frame.img_gray

            #size of img
            height, width = np.shape(img_gray)

            #basic parameters
            ROI_weight = [0.44, 0.14, 0.14, 0.14, 0.14]
            zoom_factor = 16
            ROI_linewidth = int(height // 300)
            text_position = 'Contrast'

            #    print(height,width)

            #image of ROI
            this_img_ROI = np.full(np.shape(img_gray), np.nan)

            list_5_points = [[height / 2, width / 2], [height / 4, width / 4],
                             [height / 4,
                              -width / 4], [-height / 4, -width / 4],
                             [-height / 4, width / 4]]

            #size of area
            area_half_height = int(np.shape(img_gray)[0] / zoom_factor)
            area_half_width = int(np.shape(img_gray)[1] / zoom_factor)

            list_VCM_code_fine.append(this_frame.VCM_code)

            #calculate contrast in each area
            list_contrast_5_areas = []

            if ROI_mode == '5-Area':

                for i, j in list_5_points:

                    this_area = img_gray[int(i) - area_half_height:int(i) +
                                         area_half_height,
                                         int(j) - area_half_width:int(j) +
                                         area_half_width]

                    #collect it
                    list_contrast_5_areas.append(
                        C_C.GlobalContrast(this_area, operator))

                    #draw the bound of ROI
                    for k in range(ROI_linewidth):

                        this_img_ROI[int(i - k) - area_half_height,
                                     int(j - k) -
                                     area_half_width:int(j + k + 1) +
                                     area_half_width] = 1
                        this_img_ROI[int(i + k) + area_half_height,
                                     int(j - k) -
                                     area_half_width:int(j + k + 1) +
                                     area_half_width] = 1
                        this_img_ROI[int(i - k) -
                                     area_half_height:int(i + k + 1) +
                                     area_half_height,
                                     int(j - k) - area_half_width] = 1
                        this_img_ROI[int(i - k) -
                                     area_half_height:int(i + k + 1) +
                                     area_half_height,
                                     int(j + k) + area_half_width] = 1

                #collect the data
                list_contrast_fine.append(
                    np.sum(
                        np.array(ROI_weight) *
                        np.array(list_contrast_5_areas)))
                list_img_ROI_fine.append(this_img_ROI)

            if ROI_mode == 'Center':

                for i, j in list_5_points[:1]:

                    this_area = img_gray[int(i) - area_half_height:int(i) +
                                         area_half_height,
                                         int(j) - area_half_width:int(j) +
                                         area_half_width]

                    #collect it
                    list_contrast_5_areas.append(
                        C_C.GlobalContrast(this_area, operator))

                    #draw the bound of ROI
                    for k in range(ROI_linewidth):

                        this_img_ROI[int(i - k) - area_half_height,
                                     int(j - k) -
                                     area_half_width:int(j + k + 1) +
                                     area_half_width] = 1
                        this_img_ROI[int(i + k) + area_half_height,
                                     int(j - k) -
                                     area_half_width:int(j + k + 1) +
                                     area_half_width] = 1
                        this_img_ROI[int(i - k) -
                                     area_half_height:int(i + k + 1) +
                                     area_half_height,
                                     int(j - k) - area_half_width] = 1
                        this_img_ROI[int(i - k) -
                                     area_half_height:int(i + k + 1) +
                                     area_half_height,
                                     int(j + k) + area_half_width] = 1

                #collect the data
                list_contrast_fine.append(list_contrast_5_areas[0])
                list_img_ROI_fine.append(this_img_ROI)

            print('--> VCM Code:', this_frame.VCM_code)
            # print('---> Contrast:',list_contrast_fine[-1])
    '''combine coarse and fine frames'''
    list_VCM_code = list_VCM_code_coarse + list_VCM_code_fine
    list_contrast = list_contrast_coarse + list_contrast_fine

    #construct map between VCM code and contrast
    map_VCM_code_frames = dict(zip(list_VCM_code, list_contrast))

    #sort the VCM code list in an ascending order
    list_VCM_code = list(set(list_VCM_code))
    list_VCM_code.sort()

    list_contrast = list(
        O_D.DictSortByIndex(map_VCM_code_frames, list_VCM_code).values())

    #normalization of contrast list
    list_normalized_contrast = C_N_A.Normalize(list_contrast)

    plt.figure(figsize=(17, 6))
    '''input image and bound'''
    ax_input_image = plt.subplot(121)
    '''AF result: peak VCM code'''
    if flag == 'Coarse':

        peak_index = FullSweepFine(list_contrast_coarse)
        peak_VCM_code = list_VCM_code_coarse[peak_index]
        peak_normalized_contrast = C_N_A.Normalize(
            list_contrast_coarse)[peak_index]

        plt.imshow(frames_coarse[peak_index].img_gray, cmap='gray')
        plt.imshow(list_img_ROI_coarse[peak_index], cmap='seismic_r')

    if flag == 'Fine':

        peak_index = FullSweepFine(list_contrast_fine)
        peak_VCM_code = list_VCM_code_fine[peak_index]
        peak_normalized_contrast = C_N_A.Normalize(
            list_contrast_fine)[peak_index]

        plt.imshow(frames[peak_index].img_gray, cmap='gray')
        plt.imshow(list_img_ROI_fine[peak_index], cmap='seismic_r')

    print('')
    print('---> Peak VCM Code:', peak_VCM_code)

    #center of ROI
    list_5_center_ROI = [[height / 2, width / 2], [height / 4, width / 4],
                         [height / 4, 3 * width / 4],
                         [3 * height / 4, 3 * width / 4],
                         [3 * height / 4, width / 4]]

    if text_position == 'Image':

        #show ROI weight
        for kk in range(len(list_5_center_ROI)):

            #center coordinates of ROI
            y_center_ROI = list_5_center_ROI[kk][0] + height / 60
            x_center_ROI = list_5_center_ROI[kk][1] - width / 40

            ax_input_image.text(x_center_ROI,
                                y_center_ROI,
                                '%.2f' % (ROI_weight[kk]),
                                fontdict=text_prop)

    plt.title('Input Image', fontdict=title_prop)

    plt.xticks([])
    plt.yticks([])
    '''contrast curve'''
    ax_contrast_curve = plt.subplot(122)

    plt.plot(list_VCM_code,
             list_normalized_contrast,
             color=map_operator_color[operator],
             marker='.',
             markersize=8,
             linestyle='-',
             label=operator)

    #set ticks fonts
    plt.tick_params(labelsize=12)
    labels = ax_contrast_curve.get_xticklabels(
    ) + ax_contrast_curve.get_yticklabels()

    #label fonts
    [this_label.set_fontname('Times New Roman') for this_label in labels]

    plt.xlabel('Lens Position Code', fontdict=label_prop)
    plt.ylabel('Focus Value', fontdict=label_prop)

    if operator in list_contrast_operator:

        str_focus_value = operator + ' Contrast'

    if operator in list_tenengrad_operator:

        str_focus_value = operator + ' Tenengrad'

    plt.title(str_focus_value + '-Lens Position Curve', fontdict=title_prop)

    plt.legend(prop=legend_prop, loc='lower right')

    #VMC code for plotting limit
    # list_VCM_code_total=[this_frame.VCM_code for this_frame in frames_coarse]

    #limit of x and y
    x_min, x_max = -24, 1024
    y_min, y_max = 0 - .023 * 2, 1 + .023 * 2

    #tick step
    x_major_step = 100
    x_minor_step = 50
    y_major_step = 0.1
    y_minor_step = 0.05

    #axis boundary
    plt.xlim([x_min, x_max])
    plt.ylim([y_min, y_max])

    #horizontal line
    plt.hlines(peak_normalized_contrast,
               x_min,
               x_max,
               color='grey',
               linestyles="--")

    #vertical line
    plt.vlines(peak_VCM_code, y_min, y_max, color='grey', linestyles="--")

    #set locator
    ax_contrast_curve.xaxis.set_major_locator(MultipleLocator(x_major_step))
    ax_contrast_curve.xaxis.set_minor_locator(MultipleLocator(x_minor_step))
    ax_contrast_curve.yaxis.set_major_locator(MultipleLocator(y_major_step))
    ax_contrast_curve.yaxis.set_minor_locator(MultipleLocator(y_minor_step))

    #annotation of peak VCM code
    ax_contrast_curve.annotate(
        'Peak: %d' % peak_VCM_code,
        xy=(peak_VCM_code, peak_normalized_contrast),
        xytext=(peak_VCM_code + x_major_step / 10,
                peak_normalized_contrast + y_major_step / 10),
        color='k',
        fontproperties=sample_prop)

    #text of parameter
    if ROI_mode == '5-Area':

        str_text = 'ROI Zoom Factor: %d Weight: %.2f-%.2f' % (
            zoom_factor / 2, ROI_weight[0], ROI_weight[1])

    if ROI_mode == 'Center':

        str_text = 'ROI Zoom Factor: %d' % (zoom_factor / 2)

    ax_contrast_curve.text(0 + x_major_step / 10,
                           1 + y_major_step / 10,
                           str_text,
                           fontdict=text_prop)

    #save the fig
    '''operator experiment'''
    fig_path_operator = output_folder_operator + '//Peak.png'
    '''condition experiment'''
    fig_path_condition = output_folder_condition + str_c + ' ' + str_d + ' (Peak).png'

    plt.grid()

    plt.savefig(fig_path_operator, dpi=300, bbox_inches='tight')
    plt.savefig(fig_path_condition, dpi=300, bbox_inches='tight')
    plt.close()