Esempio n. 1
0
    def __init__(self, delay, contours, DEBUG):
        if DEBUG:
            calculation_log(
                'constructor of contours_analysis_results in single_image was called'
            )
        self.contours = contours
        self.delay = delay
        self.__DEBUG = DEBUG
        if DEBUG:
            calculation_log('datas were setted.')
        if len(self.contours) != 0:
            self.__set_main_Y_of_nozzle()
            self.__set_convex_hull_area_of_nozzle()
            self.__set_area_of_nozzle()
            self.__set_arclength_of_nozzle()
            self.__set_convex_hull_arclength_of_nozzle()
            self.__set_solidity_of_nozzle()
            self.__set_freq_Y_of_nozzle()
            self.__set_num_contours()
            self.__set_max_reg_length_in_delay()
            self.__set_max_mainXY()
            self.__set_satellite_suspicious_XY()
            self.__set_volume_without_nozzle()

        return None
Esempio n. 2
0
 def __set_solidity_of_nozzle(self):
     '''
     ノズルのSolidity値を取得する関数
     '''
     if self.__DEBUG:
         calculation_log('set solidity_of_nozzle')
     solidity_of_nozzle = self.convex_hull_area_of_nozzle / self.area_of_nozzle
     self.solidity_of_nozzle = solidity_of_nozzle
     return None
Esempio n. 3
0
 def __set_freq_Y_of_nozzle(self):
     '''
     ノズルの最頻出Y座標を取得する関数
     '''
     if self.__DEBUG:
         calculation_log('set freq_Y_of_nozzle')
     self.freq_Y_of_Nozzle = self.contours[0].freq_Y
     self.num_freq_Y_of_Nozzle = self.contours[0].freq_num
     return None
Esempio n. 4
0
 def __set_arclength_of_nozzle(self):
     '''
     ノズルのArclength値を取得する関数
     '''
     if self.__DEBUG:
         calculation_log('set arclength_of_nozzle')
     arclength_of_nozzle = self.contours[0].arclength
     self.arclength_of_nozzle = arclength_of_nozzle
     return None
Esempio n. 5
0
 def __set_convex_hull_area_of_nozzle(self):
     '''
     ノズルのConvex_Hull_Area値を取得する関数
     '''
     if self.__DEBUG:
         calculation_log('set convex_hull_area_of_nozzle')
     convex_hull_area_of_nozzle = self.contours[0].convex_hull_contour_area
     self.convex_hull_area_of_nozzle = convex_hull_area_of_nozzle
     return None
Esempio n. 6
0
 def __set_convex_hull_arclength_of_nozzle(self):
     '''
     ノズルのconvex_hull_arclength値を取得する関数
     '''
     if self.__DEBUG:
         calculation_log('set convex_hull_arclength_of_nozzle')
     convex_hull_arclength_of_nozzle = self.contours[
         0].convex_hull_contour_arclength
     self.convex_hull_arclength_of_nozzle = convex_hull_arclength_of_nozzle
     return None
Esempio n. 7
0
    def __set_num_contours(self):
        '''
        画像内の輪郭数をカウントする関数
        ※ノイズ除去前ゆえ、ノイズが含まれる可能性がある。
        '''
        if self.__DEBUG:
            calculation_log('set num_contours')
        num_contours = len(self.contours)
        self.num_contours = num_contours

        return None
    def __set_freq_Y(self):
        if self.__DEBUG:
            calculation_log('freq_Y method was called.')
        target_cnt = [
            cnts for cnts in self.analysisResults
            if cnts.solidity_of_nozzle == self.solidity
        ][0]
        self.freq_Y = target_cnt.freq_Y_of_Nozzle
        self.freq_Y_cnt = target_cnt.num_freq_Y_of_Nozzle

        return None
Esempio n. 9
0
    def __set_max_mainXY(self):
        '''
        メイン液滴と推定される座標を取得
        (main_Y座標最大となる輪郭のmain_X、main_Y座標を取得)
        '''
        if self.__DEBUG:
            calculation_log('set max_MainXY')
        mainXY = sorted(self.contours, key=lambda cnt: cnt.area,
                        reverse=True)[0]
        self.max_mainY = mainXY.main_Y
        self.max_mainX = mainXY.main_X

        return None
    def __set_num_initial_contour(self):
        if self.__DEBUG:
            calculation_log('num_ave_contour_method was called.')
        if self.flag_separated_is_detected:
            retNum_list = [
                len(rst.contours) for rst in self.analysisResults
                if rst.delay < self.delay_separated
            ]
        else:
            retNum_list = [len(rst.contours) for rst in self.analysisResults]
        self.num_contours_at_first = retNum_list

        return None
Esempio n. 11
0
    def __set_max_reg_length_in_delay(self):
        '''
        リガメント値が最大となる輪郭のリガメント値を取得
        '''
        if self.__DEBUG:
            calculation_log('set max_reg_length')
        set_value = -1
        if not self.num_contours < 2:
            eval_cnts = self.contours[1:]
            reg_length_list = [cnt.get_regament_length() for cnt in eval_cnts]
            set_value = max(reg_length_list)

        self.max_reg_length_in_delay = set_value
        return None
    def __set_arc_solidity(self):
        '''
        
        '''
        if self.__DEBUG:
            calculation_log('arc_solidity method was called.')
        rsts = [
            cnts.convex_hull_arclength_of_nozzle / cnts.arclength_of_nozzle
            for cnts in self.analysisResults
        ]
        arc_solidity = max(rsts)
        self.arc_solidity = arc_solidity

        return None
Esempio n. 13
0
    def __set_volume_without_nozzle(self):
        '''
        ノズル輪郭をのぞく輪郭の体積値の和を取得
        '''
        if self.__DEBUG:
            calculation_log('set volume without_nozzle')
        set_value = 0
        if not self.num_contours < 2:
            eval_cnts = self.contours[1:]
            volume_list = [cnt.volume for cnt in eval_cnts]
            set_value = max(volume_list)

        self.volume_without_nozzle = set_value
        return None
    def __set_solidity(self):
        '''
        Solidity値取得関数
        各画像の面積最大輪郭(=ノズル輪郭)の凸包輪郭面積 / 実面積比の最小値を取得する。
        ノズルに異物がついているとこの値は大きくなるので、ある閾値を越えた場合に異物があると判定する。
        詳細アルゴリズムはコメントにて記載する。
        '''
        if self.__DEBUG:
            calculation_log('solidity method was called.')
        solidity_list = [
            cnts.solidity_of_nozzle for cnts in self.analysisResults
        ]
        #クラスプロパティにセット
        self.solidity = min(solidity_list)

        return None
    def __set_volume(self):
        '''
        
        '''
        if self.__DEBUG:
            calculation_log('volume method was called.')
        lstReached = list(
            filter(
                lambda dat: len(
                    [d for d in dat.contours if d.main_Y == d.imgYMax]) != 0,
                self.analysisResults))
        if len(lstReached) == 0:
            delay_upper_limit = 1000
            reaching_delay = delay_upper_limit
        else:
            reaching_delay = list(
                filter(
                    lambda dat: len(
                        [d for d in dat.contours
                         if d.main_Y == d.imgYMax]) != 0,
                    self.analysisResults))[0].delay
        lst = [
            dat for dat in self.analysisResults
            if (dat.delay >= self.delay_separated) and (
                dat.delay < reaching_delay)
        ]
        if len(lst) == 0:
            volumeAve = -1
            volumeStdDiv = -1
        volumeList = [
            data.volume_without_nozzle for data in lst
            if len(data.contours) > 1
        ]
        if len(volumeList) == 0:
            volumeAve = 0
            volumeStdDiv = 0
        else:
            volumeAve = sum(volumeList) / len(volumeList)
            volumeStdDiv = sum([
                math.sqrt((vol - volumeAve)**2 / len(volumeList))
                for vol in volumeList
            ])
        self.volume_ave = volumeAve
        self.volume_std_div = volumeStdDiv

        return None
Esempio n. 16
0
    def __set_satellite_suspicious_XY(self):
        '''
        サテライト液滴座標と推定される座標を取得
        '''
        if self.__DEBUG:
            calculation_log('set sattelite_like_XY')
        set_num_X = 0
        set_num_Y = 0
        if self.num_contours < 2:
            set_num_X = -1
            set_num_Y = -1
        else:
            eval_cnts = sorted(self.contours, key=lambda cnt: cnt.satellite_Y)
            set_num_X = eval_cnts[1].satellite_X
            set_num_Y = eval_cnts[1].satellite_Y

        self.satellite_suspicious_X = set_num_X
        self.satellite_suspicious_Y = set_num_Y

        return None
 def __set_magnitude_of_doublet_droplet(self):
     '''
     二重吐出度計算関数
     
     '''
     if self.__DEBUG:
         calculation_log(
             'detect magnitude of doublet_droplet method was called.')
     if self.flag_separated_is_detected:
         seeking_results_list = [
             cnts.solidity_of_nozzle for cnts in self.analysisResults
             if cnts.delay >= self.delay_separated
         ]
         if len(seeking_results_list) == 1:
             self.magnitude_of_doublet_droplet = -1
         else:
             diff_solidity_after_separation = max(
                 seeking_results_list) - min(seeking_results_list)
             self.magnitude_of_doublet_droplet = diff_solidity_after_separation
     else:
         self.magnitude_of_doublet_droplet = -1
     return None
    def __set_droplet_separated(self):
        '''
        液滴分離検知関数
        液滴吐出開始を検知し、吐出有無およびそのディレイタイムをflag_separated_is_detected、およびdelay_separatedに値をセットする。        
        分離検知が無い場合はdelay_separated = -1.0
        詳細アルゴリズムはコメントにて記載
        '''
        if self.__DEBUG:
            calculation_log('separation method was called.')
        delay_separated = -1.0
        flag_separated_is_detected = False
        seeking_results_list = [
            data for data in self.analysisResults
            if data.delay >= self.delay_faced
        ]
        solidity_base = self.solidity
        for i in range(1, len(seeking_results_list)):
            eval_delay = seeking_results_list[i].delay
            flag_convexhull_contour_area = (seeking_results_list[i].convex_hull_area_of_nozzle +\
                                            self.__area_mirgin_detect_separation < seeking_results_list[i-1].convex_hull_area_of_nozzle)
            flag_convexhull_contour_arclength = (seeking_results_list[i].arclength_of_nozzle + self.__arc_length_mirgin_detect_separation <\
                                                 seeking_results_list[i-1].arclength_of_nozzle)
            solidity_at_seeking_result = seeking_results_list[
                i].solidity_of_nozzle
            eval_ratio = solidity_at_seeking_result / solidity_base
            flag_solidity_is_plausible = eval_ratio < self.__solidity_mirgin_detect_separation
            flag_length_of_contours = (len(seeking_results_list[i].contours) >
                                       1)
            if self.__DEBUG:
                calculation_log(
                    'delay at {}, solidity is {}, eval ratio is {}, and flag is {}'
                    .format(eval_delay, solidity_at_seeking_result, eval_ratio,
                            flag_solidity_is_plausible))

            if flag_convexhull_contour_area and flag_convexhull_contour_arclength and flag_solidity_is_plausible and flag_length_of_contours:
                delay_separated = eval_delay
                flag_separated_is_detected = True
                break
        if self.__DEBUG:
            calculation_log('separated_delay is {}'.format(delay_separated))
        self.flag_separated_is_detected = flag_separated_is_detected
        self.delay_separated = delay_separated

        return None
    def __set_droplet_faced(self):
        '''
        液滴吐出開始検知関数
        液滴吐出開始を検知し、吐出有無およびそのディレイタイムをflag_faced_is_detected、およびdelay_facedに値をセットする。        
        吐出開始検知が無い場合はdelay_faced = -1.0
        詳細アルゴリズムはコメントにて記載。
        '''
        if self.__DEBUG:
            calculation_log('detect_faced method was called.')
        #初期値のセット
        delay_faced = -1.0
        flag_faced_is_detected = False
        #
        for i in range(1, len(self.analysisResults)):
            evaluation_delay = self.analysisResults[i].delay
            before_convex_hull_area = self.analysisResults[
                i - 1].convex_hull_area_of_nozzle
            after_convex_hull_area = self.analysisResults[
                i].convex_hull_area_of_nozzle
            flag_detection = after_convex_hull_area - self.__area_mirgin_detect_faced > before_convex_hull_area
            if self.__DEBUG:
                calculation_log(
                    'delay : {}, evaluation_area : {} evaluation_area_before : {}'
                    .format(evaluation_delay, after_convex_hull_area,
                            before_convex_hull_area))

            if flag_detection:
                delay_faced = evaluation_delay
                flag_faced_is_detected = True
                if self.__DEBUG:
                    calculation_log(
                        'detect_faced delay is {}.'.format(delay_faced))
                break

        self.flag_faced_is_detected = flag_faced_is_detected
        self.delay_faced = delay_faced

        return None
Esempio n. 20
0
def drawContours(delay,
                 path,
                 min_area_thresh,
                 binarize_thresh,
                 auto_binarize,
                 draw_convexhull,
                 draw_coords,
                 exportImage,
                 draw_reg_Detected,
                 mkdir,
                 DEBUG):
    '''
    デバッグ時輪郭解析結果出力関数
    
    
    '''
    
    if mkdir:
        if not os.path.exists(os.path.dirname(path) + "/drawRsts"):
            os.makedirs(os.path.dirname(path) + "/drawRsts", exist_ok = True)
            if DEBUG:
                calculation_log('mkdir at {}/drawRsts'.format(os.path.dirname(path)))

    im = cv2.imread(path)
    imGray = cv2.imread(path,0)
     #二値化、オートバイナライズか否かで挙動違う
    ret, im_bin = cv2.threshold(
        imGray,
        binarize_thresh, 
        255, 
        (cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) if auto_binarize else (cv2.THRESH_BINARY_INV))
    #輪郭抽出
    contours, hierarchy = cv2.findContours(im_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    #閾値でフィルタ
    contours = [cnt for cnt in contours if cv2.contourArea(cnt) > min_area_thresh].sort(
        key=lambda cnt: min(cnt, key = lambda pt2:pt2[0,1])[0,1])
    im = cv2.drawContours(im, [contours[0]], -1, (0,255,0), 2)
    if len(contours) > 1:
        im = cv2.drawContours(im, contours[1:], -1, (0,255,0), 1)
    if draw_convexhull:
        hulls = [cv2.convexHull(cnt) for cnt in contours]
        im = cv2.drawContours(im, [hulls[0]], -1, (255,255,0), 2)
        if len(hulls) > 1:
            im = cv2.drawContours(im, hulls[1:], -1, (255,255,0), 1)
    
    if draw_coords:
        coords = analyse_Image(
            delay = delay,
            path=path,
            min_area_thresh = min_area_thresh,
            binarize_thresh=binarize_thresh,
            auto_binarize = auto_binarize
        )
        symbol_size = 5
        for i in range(len(coords)):
            main_X = coords[i].main_X
            main_Y = coords[i].main_Y       
            im = cv2.rectangle(im,
                               ((int)(main_X-symbol_size),(int)(main_Y-symbol_size)),
                               ((int)(main_X+symbol_size),(int)(main_Y+symbol_size)),
                               (0,0,255),
                               2 if i == 0 else 1)
            satellite_X = coords[i].satellite_X
            satellite_Y = coords[i].satellite_Y
            im = cv2.rectangle(im,
                               ((int)(satellite_X-symbol_size),(int)(satellite_Y-symbol_size)),
                               ((int)(satellite_X+symbol_size),(int)(satellite_Y+symbol_size)),
                               (0,255,255),
                               2 if i == 0 else 1)
            center_X = coords[i].center_X
            center_Y = coords[i].center_Y
            im = cv2.rectangle(im,
                               ((int)(center_X-symbol_size),(int)(center_Y-symbol_size)),
                               ((int)(center_X+symbol_size),(int)(center_Y+symbol_size)),
                               (255,255,255),
                               2 if i == 0 else 1)            
       
    savePath = (os.path.dirname(path) + '/drawRsts/'+ os.path.splitext(os.path.basename(path))[0] + \
                '_drawResult.jpg') if mkdir else (os.path.splitext(path)[0] + "_drawResult.jpg")
    cv2.imwrite(savePath, im)
    del im
    if DEBUG:
        calculation_log('export debug image at {}'.format(savePath))
    return None
Esempio n. 21
0
def drawTrackingResult(dirPath, trackingRsts, draw_Fitting_Line, DEBUG):
    '''
    追尾結果描画関数。privateメソッド。
    指定ディレクトリ内に「tracikngRsts」ディレクトリを生成し、その中に
    対応する追尾結果を描画する。
    画像は.jpgのみ。
    
    Parameters
    ----------
    dirPath : str
        描画対象画像格納フォルダ
    trackingRsts : Tracking_Resultsクラス
        描画対象結果格納追尾結果クラス
    draw_Fitting_Line : bool
        フィッティング結果描画の有無を指定
    DEBUG : bool
        デバッグモード指定
            
    Return
    ----------
        なし
    
    '''
    extension = '.jpg'
    pathes = glob.glob(dirPath + "/*" + extension)
    if os.path.exists(dirPath + "/trackingRsts"):
        delPathes = glob.glob(dirPath + "/trackingRsts/*.jpg")
        for f in delPathes:
            os.remove(f)
    os.makedirs(dirPath + "/trackingRsts", exist_ok=True)
    if DEBUG:
        calculation_log('mkdir at {}/trackingRsts'.format(dirPath))

    signature_size = 5

    for i in range(len(pathes)):
        f = pathes[i]
        #画像データの読み込み、opencv→numpyデータアレイ化
        im = cv2.imread(f)

        #ファイル名からディレイ時間を読み取り
        delay = 0.0
        max_delay_description_field_length = 10
        j = max_delay_description_field_length
        flag_delay_is_get = False
        while j > len(extension):
            try:
                delay = float(f[-j:-len(extension)])
                flag_delay_is_get = True
                break
            except:
                j = j - 1

        if not flag_delay_is_get:
            delay = -1.0
            print("delay_not_estimated at {}".format(f))

        if (float)(delay) in [(float)(de.delay) for de in trackingRsts.results
                              if not math.isnan(de.main_X)]:
            x = [
                rst for rst in trackingRsts.results
                if (float)(rst.delay) == (float)(delay)
            ][0].main_X
            y = [
                rst for rst in trackingRsts.results
                if (float)(rst.delay) == (float)(delay)
            ][0].main_Y
            im = cv2.rectangle(im,((int)(x-signature_size),(int)(y-signature_size)),((int)(x+signature_size),(int)(y+signature_size)),\
                               (0,0,255),3)

        if (float)(delay) in [(float)(de.delay) for de in trackingRsts.results
                              if not math.isnan(de.satellite_X)]:
            x = [
                rst for rst in trackingRsts.results
                if (float)(rst.delay) == (float)(delay)
            ][0].satellite_X
            y = [
                rst for rst in trackingRsts.results
                if (float)(rst.delay) == (float)(delay)
            ][0].satellite_Y
            im = cv2.rectangle(im,((int)(x-signature_size),(int)(y-signature_size)),((int)(x+signature_size),(int)(y+signature_size)),\
                               (255,0,0),3)
        if draw_Fitting_Line == True:
            lineMainResults, lineSatelliteResults = trackingRsts.get_Main_vector_slope_intercept(
            ), trackingRsts.get_Satellite_vector_slope_intercept()
            if not any([math.isnan(value) for value in lineMainResults]):
                im = cv2.line(im, ((int)(lineMainResults[5]), 0),
                              ((int)(lineMainResults[4] * im.shape[0] +
                                     lineMainResults[5]), im.shape[0]),
                              (64, 64, 128), 1)
                predMainX = (int)(lineMainResults[0] * delay +
                                  lineMainResults[1])
                predMainY = (int)(lineMainResults[2] * delay +
                                  lineMainResults[3])
                im = cv2.circle(im, (predMainX, predMainY), signature_size,
                                (64, 64, 128), 1)
            if not any([math.isnan(value) for value in lineSatelliteResults]):
                im = cv2.line(im, ((int)(lineSatelliteResults[5]), 0),
                              ((int)(lineSatelliteResults[4] * im.shape[0] +
                                     lineSatelliteResults[5]), im.shape[0]),
                              (128, 64, 64), 1)
                predSatX = (int)(lineSatelliteResults[0] * delay +
                                 lineSatelliteResults[1])
                predSatY = (int)(lineSatelliteResults[2] * delay +
                                 lineSatelliteResults[3])
                im = cv2.circle(im, (predSatX, predSatY), signature_size,
                                (128, 64, 64), 2)
        savePath = dirPath + "/trackingRsts/" + os.path.splitext(
            os.path.basename(f))[0] + "_drawTRResults.jpg"
        cv2.imwrite(savePath, im)

        if DEBUG:
            calculation_log(
                'tracking result file is exported at {}'.format(savePath))

    return None
def get_autoTracking_Results(directory_path, camera_resolution, API_VER,
                             exec_mode):
    '''
    追尾→特徴量抽出関数   
    ・パラメータ:dirpath : str
                    ディレクトリパス
                camera_resolution : float
                    カメラ分解能、[um/pix]
                exec_mode : str
                    起動モード、DEBUGでデバッグモード、文字列等出力


    '''

    flag_is_debugmode = (exec_mode == DEBUG_MODE)  #デバッグモードの指定確認。
    if flag_is_debugmode:
        calculation_log('analyse dir path = {}'.format(directory_path))
        calculation_log('exec_mode = {}'.format(exec_mode))
    flag_contour_select_was_done = False
    if flag_is_debugmode:
        calculation_log('contour selection function is calling')

    #輪郭抽出計算の実施。
    try:
        contour_rsts, flag_image_modified_deeply = FindContourAnalysis.analyse_Images_List(
            directoryPath=directory_path,
            min_area_thresh=input_params_dic["min_area_thresh"],
            binarize_thresh=input_params_dic["binarize_thresh"],
            auto_binarize=input_params_dic["auto_binarize"],
            area_mirgin_detect_faced=input_params_dic["areaThresh_faced"],
            area_mirgin_detect_separation=input_params_dic[
                "areaThresh_separated"],
            arc_length_mirgin_detect_separation=input_params_dic[
                "arclengthThresh"],
            solidity_mirgin_detect_separation=input_params_dic[
                "solidity_ratio_thresh"],
            noise_remove_topological_dist_thresh=input_params_dic[
                "noise_remove_topological_dist_thresh"],
            noise_remove_area_thresh=input_params_dic[
                "noise_remove_area_thresh"],
            mkdir=flag_is_debugmode,
            draw_contour=flag_is_debugmode,
            draw_convexhull=flag_is_debugmode,
            draw_coords=flag_is_debugmode,
            exportImage=flag_is_debugmode,
            draw_reg_Detected=flag_is_debugmode,
            DEBUG=flag_is_debugmode)

        flag_contour_select_was_done = True
        if flag_is_debugmode:
            calculation_log('contour selection was done')
        if contour_rsts is None:
            result = {
                "analysis_date_time":
                nan_to_minus1(
                    datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S')),
                "file_name":
                nan_to_minus1(directory_path),
                "condition":
                "num_files are not more than 2.",
                "camera_resolution[um/pix]":
                nan_to_minus1(camera_resolution),
                "API_VER":
                nan_to_minus1(API_VER),
                "CONTOUR_CODE_VER":
                nan_to_minus1(FindContourAnalysis.get_code_ver()),
                "AUTO_TRACKING_CODE_VER":
                nan_to_minus1(AutoTracking.get_code_ver()),
                "THRESH_VALUES_VER":
                THRESH_VALUES_VER,
            }
            return result

    #輪郭抽出失敗時処理
    except:
        if flag_is_debugmode:
            calculation_log('contour_selection was failure.')
        result = {
            "analysis_date_time":
            datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S'),
            "filename":
            directory_path.split('/')[-1],
            'condition':
            'contour selection was failure.'
        }

    #輪郭抽出成功→疑似追尾実行
    if flag_contour_select_was_done:
        flag_tracking_was_done = False
        #疑似追尾実施。
        try:
            trackingResults = AutoTracking.auto_Tracking(
                dirPath=directory_path,
                rsts=contour_rsts,
                draw_Tracking_Results=flag_is_debugmode,
                draw_Fitting_Line=flag_is_debugmode,
                DEBUG=flag_is_debugmode)
            flag_tracking_was_done = True
            if flag_is_debugmode:
                calculation_log('auto_tracking was done')
        #疑似追尾失敗時処理
        except:
            if flag_is_debugmode:
                calculation_log('auto_tracking was failed')
            result = {
                "analysis_date_time":
                datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S'),
                "filename":
                directory_path.split('/')[-1],
                'condition':
                'auto_tracking was failure.'
            }

        ##以下、吐出開始、分離検知ディレイ修正コマンド
        ##テスト版につきしばらく実行しない。

        #疑似追尾成功→特徴量計算
        if flag_tracking_was_done:
            try:
                #出力用の値の整理
                #ファイル名取得
                directory_path_show = directory_path.split('/')[-1]
                if flag_is_debugmode:
                    calculation_log(
                        'dir_path is {}'.format(directory_path_show))

                #1枚目輪郭数取得
                num_first_contours = (float)(sum(
                    contour_rsts.num_contours_at_first)) / (float)(len(
                        contour_rsts.num_contours_at_first))
                if flag_is_debugmode:
                    calculation_log(
                        'num_first_contours is {}'.format(num_first_contours))
                #solidity値取得
                solidity = contour_rsts.solidity
                if flag_is_debugmode:
                    calculation_log('solidity is {}'.format(solidity))
                #arc_solidity値取得
                arc_solidity = contour_rsts.arc_solidity
                if flag_is_debugmode:
                    calculation_log('arc_solidity is {}'.format(arc_solidity))
                #液滴吐出開始検知有無およびディレイ取得
                flag_droplet_faced, faced_delay = contour_rsts.flag_faced_is_detected, contour_rsts.delay_faced
                if flag_is_debugmode:
                    calculation_log(
                        'faced_delay is {} and flag_droplet_faced is {}'.
                        format(faced_delay, flag_droplet_faced))
                #液滴分離検知有無およびディレイ取得
                flag_droplet_separated, separated_delay = contour_rsts.flag_separated_is_detected, contour_rsts.delay_separated
                if flag_is_debugmode:
                    calculation_log(
                        'separated delay is {} and flag_separated_delay is {}'.
                        format(separated_delay, flag_droplet_separated))
                #最大リガメント長さおよびディレイ取得
                max_regament_length_delay, max_regament_length = contour_rsts.delay_max_regament_detected, contour_rsts.max_regament_length
                if flag_is_debugmode:
                    calculation_log(
                        'max_remament_length_delay is {} and the length is {}'.
                        format(max_regament_length_delay, max_regament_length))
                #メイン液滴平均速度および標準偏差取得
                main_average_velocity, main_velocity_stdiv = trackingResults.Get_Main_Velocity(
                )
                if flag_is_debugmode:
                    calculation_log(
                        'main_ave_velocity is {} and main_velocity_stdiv is {}'
                        .format(main_average_velocity, main_velocity_stdiv))
                #メイン液滴角度取得
                main_angle = trackingResults.Get_Main_Angle_degrees()
                if flag_is_debugmode:
                    calculation_log('main_angle is {}'.format(main_angle))
                #サテライト液滴平均速度および標準偏差取得
                satellite_average_velocity, satellite_velocity_stdiv = trackingResults.Get_Satellite_Velocity(
                )
                if flag_is_debugmode:
                    calculation_log(
                        'satellite_ave_velocity is {} and satellite_velocity_stdiv is {}'
                        .format(satellite_average_velocity,
                                satellite_velocity_stdiv))
                #サテライト液滴角度取得
                satellite_angle = trackingResults.Get_Satellite_Angle_degrees()
                if flag_is_debugmode:
                    calculation_log(
                        'satellite_angle is {}'.format(satellite_angle))
                #実メイン液滴速度取得
                main_average_velocity_stdized = main_average_velocity * camera_resolution
                if flag_is_debugmode:
                    calculation_log('main_ave_vel_stdized is {}'.format(
                        main_average_velocity_stdized))
                #実サテライト液滴速度取得
                satellite_average_velocity_stdized = satellite_average_velocity * camera_resolution
                if flag_is_debugmode:
                    calculation_log('satellite_ave_vel_stdized is {}'.format(
                        satellite_average_velocity_stdized))
                #メイン-サテライト液滴角度差取得
                diff_angle = abs(
                    inf_to_value(main_angle) - inf_to_value(satellite_angle))
                if flag_is_debugmode:
                    calculation_log('diff_angle is {}'.format(diff_angle))
                #逸脱液滴検出
                flag_exotic_droplet, max_exotic_area = contour_rsts.Check_exotic_droplet_exists(
                    trackingResults, thresh_values_dic['pix_mirgin'])
                if flag_is_debugmode:
                    calculation_log('flag exotic droplet is {}'.format(
                        flag_exotic_droplet))
                #5分割領域ごとの各液滴速度、標準偏差取得
                y_max = contour_rsts.analysisResults[0].contours[0].imgYMax
                if flag_is_debugmode:
                    calculation_log('y_max = {}'.format(y_max))
                y_diff = (float)(y_max) / (float)(num_div_velocity_layer)
                if flag_is_debugmode:
                    calculation_log('y_diff = {}'.format(y_diff))
                vel_add_dics = []
                for i in range(num_div_velocity_layer):
                    base = i * y_diff
                    top = (i + 1) * y_diff
                    main_vel_res = trackingResults.Get_Main_Velocity(base, top)
                    sat_vel_res = trackingResults.Get_Satellite_Velocity(
                        base, top)
                    vel_add_dics.append({
                        "layered_main_{}_ave[pix/us]".format(i):
                        nan_to_minus1(main_vel_res[0]),
                        "layered_main_{}_stdiv[pix/us]".format(i):
                        nan_to_minus1(main_vel_res[1]),
                        "layered_sat_{}_ave[pix/us]".format(i):
                        nan_to_minus1(sat_vel_res[0]),
                        "layered_sat_{}_ave[pix/us]".format(i):
                        nan_to_minus1(sat_vel_res[1]),
                    })
                    if flag_is_debugmode:
                        calculation_log(
                            'dic_elem_{} was generated as {}'.format(
                                i, vel_add_dics[i]))

                #以下、テストコード
                leg_ref = (nan_to_minus1(contour_rsts.delay_separated) -
                           nan_to_minus1(contour_rsts.delay_faced)
                           ) * nan_to_minus1(main_average_velocity)
                leg_eval_ratio = nan_to_minus1(leg_ref) / nan_to_minus1(
                    max_regament_length)
                print("leg_ref_is calculated")
                if nan_to_minus1(leg_eval_ratio) < 0:
                    leg_eval_ratio = -1
                contour_rsts.modify_faced_delay_and_freq_Y(trackingResults)
                contour_rsts.modify_separated_delay(trackingResults)
                print('modified_calculated')
                try:
                    max_main_angle_disturbance = max([
                        abs(angle_elem - main_angle) for angle_elem in
                        contour_rsts.get_main_angle_list(trackingResults)
                    ])
                    max_satellite_angle_disturbance = max([
                        abs(angle_elem - satellite_angle) for angle_elem in
                        contour_rsts.get_satellite_angle_list(trackingResults)
                    ])
                except:
                    max_main_angle_disturbance = -1
                    max_satellite_angle_disturbance = -1
                try:
                    if flag_is_debugmode:
                        print('draw start, directory_path is {}'.format(
                            directory_path))
                        if not os.path.exists(directory_path +
                                              "/drawModifiedNozzleXY"):
                            print('makedirs start for {}'.format(
                                directory_path + "/drawModifiedNozzleXY"))
                            os.makedirs(directory_path +
                                        "/drawModifiedNozzleXY",
                                        exist_ok=True)
                            if flag_is_debugmode:
                                calculation_log(
                                    'mkdir at {}/drawModifiedNozzleXY'.format(
                                        directory_path))
                        print("globbing")
                        pathes = glob.glob(directory_path + "/*.jpg")
                        print("globed")
                        signature_size = 5
                        print("path is {}".format(pathes[0]))
                        im = cv2.imread(pathes[0])
                        print("imread")
                        x = contour_rsts.modified_freq_X
                        y = contour_rsts.modified_freq_Y
                        im = cv2.rectangle(im,((int)(x-signature_size),(int)(y-signature_size)),((int)(x+signature_size),(int)(y+signature_size)),\
                               (0,0,255),3)
                        print('imdrawn')
                        savePath = directory_path + "/drawModifiedNozzleXY/" + os.path.splitext(
                            os.path.basename(pathes[0]))[0] + "_modifiedXY.jpg"
                        print('export succeed.')
                        cv2.imwrite(savePath, im)
                        del im
                except:
                    calculation_log('drawing_modofied_XY_failure')

                #テストコード、ここまで

                #dictionaryへの出力
                result = {
                    "analysis_date_time":
                    nan_to_minus1(
                        datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S')),
                    "file_name":
                    nan_to_minus1(directory_path_show),
                    "condition":
                    "calculation was done.",
                    "camera_resolution[um/pix]":
                    nan_to_minus1(camera_resolution),
                    "API_VER":
                    nan_to_minus1(API_VER),
                    "ave_num_contours_before_separation":
                    nan_to_minus1(num_first_contours),
                    "solidity[U]":
                    nan_to_minus1(solidity),
                    "arc_solidity[u]":
                    nan_to_minus1(arc_solidity),
                    "flag_droplet_faced":
                    nan_to_minus1(flag_droplet_faced),
                    "faced_delay[us]":
                    nan_to_minus1(faced_delay),
                    "flag_droplet_separated":
                    nan_to_minus1(flag_droplet_separated),
                    "separated_delay[us]":
                    nan_to_minus1(separated_delay),
                    "max_regament_delay[us]":
                    nan_to_minus1(max_regament_length_delay),
                    "max_regament_length[pix]":
                    nan_to_minus1(max_regament_length),
                    "most_freq_Y[pix]":
                    nan_to_minus1(contour_rsts.freq_Y),
                    "CONTOUR_CODE_VER":
                    nan_to_minus1(FindContourAnalysis.get_code_ver()),
                    "main_average_velocity[pix/us]":
                    nan_to_minus1(main_average_velocity),
                    "main_average_velocity[m/s]":
                    nan_to_minus1(main_average_velocity_stdized),
                    "main_velocity_stddiv[pix/us]":
                    nan_to_minus1(main_velocity_stdiv),
                    "main_angle[degrees]":
                    nan_to_minus1(inf_to_value(main_angle)),
                    "satellite_average_velocity[pix/us]":
                    nan_to_minus1(satellite_average_velocity),
                    "satellite_average_velocity[m/s]":
                    nan_to_minus1(satellite_average_velocity_stdized),
                    "satellite_velocity_stddiv[pix/us]":
                    nan_to_minus1(satellite_velocity_stdiv),
                    "satellite_angle[degrees]":
                    nan_to_minus1(inf_to_value(satellite_angle)),
                    "main-satellite_angle_diff[degrees]":
                    nan_to_minus1(diff_angle),
                    "main_linearity_error[pix]":
                    nan_to_minus1(
                        inf_to_value(trackingResults.
                                     Get_MainXY_Fit_Error_Min_Max_Range()[1]) -
                        inf_to_value(trackingResults.
                                     Get_MainXY_Fit_Error_Min_Max_Range()[0])),
                    "satellite_linearity_error[pix]":
                    nan_to_minus1(
                        inf_to_value(trackingResults.
                                     Get_SatelliteXY_Fit_Error_Min_Max_Range()
                                     [1]) -
                        inf_to_value(trackingResults.
                                     Get_SatelliteXY_Fit_Error_Min_Max_Range()
                                     [0])),
                    "AUTO_TRACKING_CODE_VER":
                    nan_to_minus1(AutoTracking.get_code_ver()),
                    "anormaly_ejections_at_first_image":
                    nan_to_minus1(num_first_contours > 1.5),
                    "main_velocity_is_too_fast":
                    nan_to_minus1(main_average_velocity_stdized >
                                  thresh_values_dic['velocity_upperthresh']),
                    "main_velocity_is_too_slow":
                    nan_to_minus1(main_average_velocity_stdized <
                                  thresh_values_dic['velocity_lowerthresh']),
                    "nozzle_needs_to_be_clean":
                    nan_to_minus1(
                        solidity > thresh_values_dic['Solidity_upperthresh']),
                    "suspicious_visco-elasticity":
                    nan_to_minus1(
                        (separated_delay > thresh_values_dic["sep_thresh"])
                        and (main_average_velocity_stdized >
                             thresh_values_dic["velocity_septhresh"])),
                    "angle-diff_is_too_large":
                    nan_to_minus1((
                        diff_angle > thresh_values_dic["diff_angle_thresh"]
                    ) if not math.isnan(diff_angle) else True),
                    "exotic-droplet_exists":
                    nan_to_minus1(flag_exotic_droplet),
                    "THRESH_VALUES_VER":
                    THRESH_VALUES_VER,
                    "RESERVED0":
                    "layered_main_4_ave : leg_eval_ratio, layered_main_4_stdiv : max_exotic_area, layered_sat_4_ave : flag_image_modified_deeply, layered_sat_4_stdiv : max_main_angle_disturbance, layered_main_3_ave : max_satellite_angle_disturbance, layered_main_3_stdiv : modified_delay_faced, layered_sat_3_ave : modified_delay_separated, layered_sat_3_stdiv : modified_freq_Y, layered_main_2_ave : modified_freq_X",
                    "RESERVED1":
                    "aaa",
                    "RESERVED2":
                    "aaa",
                    "RESERVED3":
                    "aaa",
                    "RESERVED4":
                    "aaa",
                }
                #result = add_message(result)

                if flag_is_debugmode:
                    calculation_log('json elem was exported.')
                #5分割各液滴速度 - 標準偏差結果追記
                for dic_elem in vel_add_dics:
                    result.update(dic_elem)
                result.update({
                    "layered_main_4_ave[pix/us]":
                    nan_to_minus1(inf_to_value(leg_eval_ratio)),
                    "layered_main_4_stdiv[pix/us]":
                    nan_to_minus1(max_exotic_area),
                    "layered_sat_4_ave[pix/us]":
                    1 if flag_image_modified_deeply else 0,
                    "layered_sat_4_stdiv[pix/us]":
                    nan_to_minus1(max_main_angle_disturbance),
                    "layered_main_3_ave[pix/us]":
                    nan_to_minus1(max_satellite_angle_disturbance),
                    "layered_main_3_stdiv[pix/us]":
                    nan_to_minus1(contour_rsts.modified_delay_faced),
                    "layered_sat_3_ave[pix/us]":
                    nan_to_minus1(contour_rsts.modified_delay_separated),
                    "layered_sat_3_stdiv[pix/us]":
                    nan_to_minus1(contour_rsts.modified_freq_Y),
                    "layered_main_2_ave[pix/us]":
                    nan_to_minus1(contour_rsts.modified_freq_X),
                })
                if flag_is_debugmode:
                    calculation_log(result)
            #計算失敗時処理
            except:
                if flag_is_debugmode:
                    calculation_log('get_feature_params was failure.')
                result = {
                    "analysis_date_time":
                    datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S'),
                    "filename":
                    directory_path,
                    'condition':
                    'get_feature_params was failure.'
                }
    return result
    def Check_exotic_droplet_exists(self, tracking_Results, pix_mirgin):
        '''
        エキゾチック輪郭(逸脱輪郭)検出関数
        
        Parameters
        ----------        
        tracking_Results : Tracking_Resultsクラス
            追尾結果クラス
        pix_mirgin : int
            逸脱判定時画像上マージン値[pix]、必ず正の値をとる。
        
        Return
        ----------
        flag_exotic_droplet_exists : bool
            逸脱輪郭検出有無、Trueで逸脱輪郭検出

        
        '''
        if self.__DEBUG:
            calculation_log('eval cnts is called.')
        #追尾結果より、メイン-サテライト液滴トラッキング近似結果の取得
        main_slope_params = tracking_Results.get_Main_vector_slope_intercept()
        sat_slope_params = tracking_Results.get_Satellite_vector_slope_intercept(
        )
        if self.__DEBUG:
            calculation_log(
                'slopes were calculated. main : {}, sat: {}'.format(
                    main_slope_params, sat_slope_params))
        #逸脱輪郭検出フラグの宣言
        flag_exotic_droplet_exists = False
        cnt_exotic_areas = []
        for cnt_list in self.analysisResults:
            flag_exotic_droplet_detected = False
            if self.__DEBUG:
                calculation_log('cnt_list with delay {} is called'.format(
                    cnt_list.delay))
            if len(cnt_list.contours) == 1:
                if self.__DEBUG:
                    calculation_log('length is 1.')
                continue
            #通常輪郭存在領域の指定
            #max_x:X座標右端、サテライトかメイン液滴x座標 + mirgin値の最大値
            max_x = max([main_slope_params[0] * cnt_list.delay + main_slope_params[1] + pix_mirgin,\
                         sat_slope_params[0] * cnt_list.delay + sat_slope_params[1] + pix_mirgin])
            #min_x:X座標左端、サテライトかメイン液滴x座標 - mirgin値の最大値
            min_x = min([main_slope_params[0] * cnt_list.delay + main_slope_params[1] - pix_mirgin,\
                         sat_slope_params[0] * cnt_list.delay + sat_slope_params[1] - pix_mirgin])
            #max_y:Y座標下端、メイン液滴y座標 + mirgin値、または画像下端Y座標の最大値
            max_y = min([main_slope_params[2] * cnt_list.delay + main_slope_params[3] + pix_mirgin,\
                         cnt_list.contours[0].imgYMax])
            #max_y:Y座標上端、サテライト液滴y座標 + mirgin値、または画像上端Y座標の最小値
            min_y = max([sat_slope_params[2] * cnt_list.delay + sat_slope_params[3] - pix_mirgin,\
                         0])

            #吐出輪郭の検証
            #ノズル輪郭は除く(ゆえにレンジは1からスタート)
            for i in range(1, len(cnt_list.contours)):
                cnt = cnt_list.contours[i]
                if self.__DEBUG:
                    calculation_log(
                        '{} th cnt with delay at {} is called'.format(
                            i, cnt_list.delay))
                #各輪郭の重心座標が上記4点で定義される矩形範囲に入るか否かを判定する。
                flag_exotic_droplet_detected = (cnt.center_X < min_x) or (
                    cnt.center_X > max_x) or (cnt.center_Y <
                                              min_y) or (cnt.center_Y > max_y)
                if flag_exotic_droplet_detected:
                    if self.__DEBUG:
                        calculation_log(
                            'exotic droplet occured at {} us, X : {}, Y : {} at min_x : {}, max_x : {}, min_y : {}, max_y : {}'
                            .format(cnt_list.delay, cnt.center_X, cnt.center_Y,
                                    min_x, max_x, min_y, max_y))
                        flag_exotic_droplet_exists = True
                        cnt_exotic_areas.append(cnt.area)
                else:
                    if self.__DEBUG:
                        calculation_log(
                            'cnt at delay{} does not contain exotic droplets'.
                            format(cnt_list.delay))
        ret_max_areas = max(
            cnt_exotic_areas) if len(cnt_exotic_areas) != 0 else 0

        return flag_exotic_droplet_exists, ret_max_areas
Esempio n. 24
0
 def __set_main_Y_of_nozzle(self):
     if self.__DEBUG:
         calculation_log('set nozzle_main_Y')
     self.main_Y_of_nozzle = self.contours[0].main_Y
     return None
    def __remove_noise(self):
        '''
        画像ノイズ除去関数
        輪郭画像から固定位置に決まって出現する小さい輪郭(=カメラに映ったごみ)を削除。
        ノズルを除く各輪郭に対して、輪郭近似度を定義し計算。
        輪郭近似度がthresh以下のものに対して、削除判定。
        削除判定時、面積がself.noise_remove_area_thresh以上のものは削除しない。
        コンストラクタより呼び出し。
        
        Parameters
        ----------        
        なし
        
        Return
        ----------
        なし

        '''

        if self.__DEBUG:
            calculation_log('noise_remove method is called')
        removeIndexList = []
        flagAllRemoveIsSucceed = True

        #以下、削除先データの削除。
        #i,jのアサインが変わるのでこの段階では削除元のデータは削除しない。
        #検索順はi, j昇順なので、削除元のデータのindexは削除後に再度別の輪郭の削除が生じても変化はしない。
        for i in range(len(self.analysisResults)):
            #            print('{}'.format(i))
            for j in range(1, len(self.analysisResults[i].contours)):
                #                print('{}'.format(j))
                flagDelDone = False
                for k in range(i + 1, len(self.analysisResults)):
                    #                    print('{}'.format(k))
                    for l in range(1, len(self.analysisResults[k].contours)):
                        #                        print('{}'.format(l))
                        if self.__get_CntDist_is_closer(
                                self.analysisResults[i].contours[j],
                                self.analysisResults[k].contours[l],
                                self.__noise_remove_topological_dist_thresh):
                            area = self.analysisResults[k].contours[l].area
                            if area < self.__noise_remove_area_thresh:
                                #                                print('{}, {}, will be deled'.format(k, l))
                                if self.__DEBUG:
                                    calculation_log(
                                        'contour with X,Y = {}, {} and area = {} was deleted as noise'
                                        .format(
                                            self.analysisResults[k].
                                            contours[l].main_X,
                                            self.analysisResults[k].
                                            contours[l].main_Y,
                                            self.analysisResults[k].
                                            contours[l].area))
                                lists = self.analysisResults[k].contours.pop(l)
                                #                               print('{}, {}, del done'.format(k, l))
                                flagDelDone = True
                                break

                if flagDelDone == True:
                    removeIndexList.append(
                        [i, j, self.analysisResults[i].contours[j]])

#        print('first_remove was done')
#以下、削除元データの削除。
        flagAllRemoveIsSucceed = False
        for removeElem in removeIndexList:
            testList = [
                cnt for cnt in self.analysisResults[removeElem[0]].contours
                if not removeElem[2].is_equal(cnt)
            ]
            if len(testList) != 0:
                self.analysisResults[removeElem[0]].contours = testList
                flagAllRemoveIsSucceed = True

        return flagAllRemoveIsSucceed
Esempio n. 26
0
def analyse_Image(delay, path, min_area_thresh, binarize_thresh, auto_binarize,
                  draw_contour, draw_convexhull, draw_coords, exportImage,
                  mkdir, flag_export_fillImg, DEBUG):
    '''
    画像読み込み→輪郭抽出→データ格納関数
    
    Parameters
    ----------------------
    delay : float
        ディレイ時間[ux]
    path : str
        ファイル名
    min_area_thresh : float
        輪郭面積最小値、この値以下はノイズと見做す。
    binarize_thresh : int
        二値化時閾値、auto_binarize = Falseで本値を利用。
    auto_binarize : bool
        自動二値化実行。大津の方法で実行する。
    draw_contour : bool
        輪郭描画フラグ
    draw_convexhull : bool
        凸包輪郭描画フラグ
    draw_coords : bool
        各座標描画フラグ
    exportImage : bool
        画像出力フラグ
    mkdir : bool
        ディレクトリ生成フラグ
    flag_export_fillImg : bool
        輪郭穴埋め結果描画フラグ
    DEBUG : bool  
        デバッグモード起動

    Returns
    ----------------------
    cntResults  : Analysis_Resultクラス
        輪郭抽出結果。画像内にある結果を一括で出力。
    '''
    if DEBUG:
        calculation_log(
            'import file is {} with the fill img flag is {}'.format(
                path, flag_export_fillImg))
    flag_image_modify_deeply = False
    #BGR画像の読み込み
    img = cv2.imread(path)
    if DEBUG:
        calculation_log('image file {} was imported'.format(path))
    #ガウシアンによるぼかし。バックグラウンドノイズの除去


#    img = cv2.GaussianBlur(img,(9, 9),3)
#ガンマ関数
#    img = gamma_function(img)
#画像のグレイスケール化
    im = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    #エッジ強調フィルタのテスト。余計な計算コストとノイズを拾いやすくなることから様子見。
    #    kernel = np.array([[-1,-1,-1],
    #                      [-1,9,-1],
    #                      [-1,-1,-1]])

    #    im = cv2.filter2D(im, -1, kernel)

    #二値化、オートバイナライズか否かで挙動違う
    ret, im_bin = cv2.threshold(im, binarize_thresh, 255,
                                (cv2.THRESH_BINARY_INV +
                                 cv2.THRESH_OTSU) if auto_binarize else
                                (cv2.THRESH_BINARY_INV))
    ret = ret - 1
    #輪郭抽出
    contours, hierarchy = cv2.findContours(im_bin, cv2.RETR_EXTERNAL,
                                           cv2.CHAIN_APPROX_SIMPLE)
    #閾値でフィルタ
    contours = [
        cnt for cnt in contours if cv2.contourArea(cnt) > min_area_thresh
    ]

    #インデックス値宣言、輪郭X座標インデックス、Y座標インデックスを指定。
    indX, indY = 0, 1
    imgYMax, _ = im.shape
    yMax_at_largest_contour = max(contours[0], key=lambda pt2: pt2[0, 1])[0, 1]
    calculation_log(
        'ymax_at_largest_contour is {}'.format(yMax_at_largest_contour))
    if yMax_at_largest_contour == imgYMax - 1:
        contours.pop(0)
        calculation_log('delete bottom noise')

    #以下、ノズル輪郭補正
    #ターゲットとなる輪郭を抽出(ノズル輪郭は必ず画像上端にあるため、輪郭のうち少なくとも1つはY = 0を含む)
    targetCnts = [
        cnt for cnt in contours if any([pt[0, indY] == 0 for pt in cnt])
    ]

    if DEBUG:
        calculation_log('the length of contour with Y = 0 point is {}'.format(
            len(targetCnts)))
    #ターゲット輪郭が存在する場合
    #targetCntがただ1つ→それはノズルがただ1つのブロブとして輪郭検出されていることを示す。
    if len(targetCnts) == 1:
        #ターゲット輪郭内部のヴォイドを検出
        #Y = 0なる輪郭点を抽出
        ptZeroList = [Pt for Pt in targetCnts[0] if Pt[0, indY] == 0]
        #Y = 0なる輪郭点のうちの最大値と最小値を検出→ノズル両端の座標
        leftX, rightX = min([pt[0, indX] for pt in ptZeroList
                             ]), max([pt[0, indX] for pt in ptZeroList])
        #理想的な輪郭抽出が実行されている場合、ノズル両端座標間の輪郭は必ず直線であるため、cv2.CHAIN_APPROX_SIMPLEでの検出では
        #ノズル両端を結ぶ輪郭内部にY = 0となる別の点が含まれることは無い。逆に、そのような点が存在する場合はノズル輪郭内部にて
        #輪郭が分割されない程度のヴォイドが発生していることを示す。
        #以下はそのようなヴォイドの有無を抽出するコード、すなわち輪郭両端座標内部(両端座標を除く)にてY == 0なる輪郭を抽出している。
        ptZeroListInnerVoid = [
            Pt for Pt in targetCnts[0] if Pt[0][indY] == 0
            and Pt[0][indX] > leftX and Pt[0][indX] < rightX
        ]
        if DEBUG:
            calculation_log('length of voided contour at nozzle is {}'.format(
                len(ptZeroListInnerVoid)))
        #ヴォイドが存在する場合
        if len(ptZeroListInnerVoid) != 0:
            #ヴォイド構成点の最も左端の座標を取得
            inner_leftX = min([pt[0, indX] for pt in ptZeroListInnerVoid])
            inner_leftPt = [
                Pt for Pt in ptZeroListInnerVoid if Pt[0][indX] == inner_leftX
            ][0]
            #ヴォイド構成点の最も右端の座標を取得
            inner_rightX = max([pt[0, indX] for pt in ptZeroListInnerVoid])
            inner_rigntPt = [
                Pt for Pt in ptZeroListInnerVoid if Pt[0][indX] == inner_rightX
            ][0]
            #targetCnts[0]輪郭構成点群における、ヴォイド最左端、最右端点のインデックスを取得。
            #以下は、targetCnts[0]内部にinner_leftPtと一致する座標を抽出する。
            #ただし、以下の関数ではnumpyの仕様から、X座標の一致とY座標の一致を別に検出してしまうため、
            #一旦ことごとく検出し、unique関数を用いて一致座標を座標一致回数とともに抽出する。
            u_left, counts_left = np.unique(
                np.where(targetCnts[0] == inner_leftPt)[0], return_counts=True)
            #X、Y座標ともに一致する場合は抽出回数が2であり、これはただ1つ存在するため
            #そのような座標を抽出した後にインデックスを取得する。
            inner_left_index = u_left[counts_left > 1][0]
            #最右端座標に関しても同様の処理をする。
            u_right, counts_right = np.unique(
                np.where(targetCnts[0] == inner_rigntPt)[0],
                return_counts=True)
            inner_right_index = u_right[counts_right > 1][0]
            #OpenCVの輪郭抽出においては、閉輪郭抽出の場合、必ずそのインデックス順序は
            #最も左上の座標を基点に反時計回りに振られる。
            #従い、ノズル輪郭上部に存在するヴォイドは、上記アルゴリズムで取得したヴォイドの最右端および最左端部インデックスの間の点である。
            voidContour = targetCnts[0][inner_right_index:inner_left_index]
            #ヴォイド構成点要素数が存在する場合(これしかありえないが、例外除け)
            if len(voidContour) != 0:
                #ヴォイド輪郭を完全に覆う最小の長方形の最小X、最大X、最大Y座標を取得する。(最小Yは必ず0である。)
                left_X_min = min([vCnt[0][0] for vCnt in voidContour])
                right_X_Max = max([vCnt[0][0] for vCnt in voidContour])
                Y = max([vCnt[0][1] for vCnt in voidContour])
                img = cv2.fillConvexPoly(img,
                                         voidContour,
                                         color=(ret, ret, ret))
                #img = cv2.rectangle(img, (left_X_min, 0), (right_X_Max, Y), (0,0,0), -1)
                if DEBUG:
                    calculation_log(
                        'img is filled with rectangle Xleft = {}, Xright = {}, Y = {}'
                        .format(left_X_min, right_X_Max, Y))
                #塗りつぶし画像出力する処理
                if flag_export_fillImg:
                    if not os.path.exists(os.path.dirname(path) + "/fillCnts"):
                        os.makedirs(os.path.dirname(path) + "/fillCnts",
                                    exist_ok=True)
                        savePath = (os.path.dirname(path) + '/fillCnts/'+ os.path.splitext(os.path.basename(path))[0] +\
                            '_fillCnts.jpg') if mkdir else (os.path.splitext(path)[0] + "_fillCnts.jpg")
                    cv2.imwrite(savePath, img)

                #以下、塗りつぶした画像に対して、グレイスケール化→二値化→輪郭抽出やり直し
                im = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                ret, im_bin = cv2.threshold(
                    im, binarize_thresh, 255,
                    (cv2.THRESH_BINARY_INV +
                     cv2.THRESH_OTSU) if auto_binarize else
                    (cv2.THRESH_BINARY_INV))
                #輪郭抽出
                contours, hierarchy = cv2.findContours(im_bin,
                                                       cv2.RETR_EXTERNAL,
                                                       cv2.CHAIN_APPROX_SIMPLE)
                #閾値でフィルタ
                contours = [
                    cnt for cnt in contours
                    if cv2.contourArea(cnt) > min_area_thresh
                ]

    #ターゲット輪郭要素数が2つ以上の場合:これは、輪郭がスプリットするくらいにノズル内部輪郭が白くなっているため
    #上記戦略が取れない。従い、分割された輪郭全てを覆うような長方形を生成し塗りつぶす。
    if len(targetCnts) > 1:
        #各ターゲット輪郭のY = 0なる点を取得
        ptZeroListList = [[Pt for Pt in tgt if Pt[0, indY] == 0]
                          for tgt in targetCnts]
        if DEBUG:
            calculation_log(
                'nozzle contour is completely separated, filling processing is now called.'
            )
        #各ターゲット輪郭の最大X座標の最大値、および最小X座標の最小値を取得(それぞれ、最左端、最右端座標となる)
        leftX = min([min([p[0, indX] for p in pts]) for pts in ptZeroListList])
        rightX = max(
            [max([p[0, indX] for p in pts]) for pts in ptZeroListList])
        #輪郭抽出近似を外し、輪郭を構成する全ての座標を抽出。
        #Y座標決定にて、直線部は近似されているため最頻出Y座標とノズルY座標が一致しない可能性が高い。
        #近似を外し、全ての構成点を取得することで、実際のY座標と最頻出Y座標を一致させる。
        #黒塗りをするY座標を、分割輪郭の面積にて重み付けした期待値として取得する。
        #最頻出Y座標は、極小面積のノイズの影響を小さくするため面積にて重み付けした。
        contours, hierarchy = cv2.findContours(im_bin, cv2.RETR_EXTERNAL,
                                               cv2.CHAIN_APPROX_NONE)
        #閾値でフィルタ
        contours = [
            cnt for cnt in contours if cv2.contourArea(cnt) > min_area_thresh
        ]
        #再度ターゲットY座標を取得
        targetCnts = [
            cnt for cnt in contours if any([pt[0, indY] == 0 for pt in cnt])
        ]

        #以下、最頻出Yと対象輪郭面積を取得
        freqNum_YList = []
        for tgtCnt in targetCnts:
            freqNum_PtList = []
            for key, ptGroup in groupby(tgtCnt, key=lambda pt: pt[0][1]):
                listPtGroup = list(ptGroup)
                freqNum_PtList.append([len(listPtGroup), key])
            freqNum_PtList = [[pt[0], pt[1]] for pt in freqNum_PtList
                              if pt[1] != 0]
            freqNum_PtList.sort(key=lambda elem: elem[0])
            freqNum_YList.append([
                freqNum_PtList[-1][0], freqNum_PtList[-1][1],
                cv2.contourArea(tgtCnt)
            ])

        #重みとY座標の積リストを取得
        YAreas = sum([lst[1] * lst[2] for lst in freqNum_YList])
        #面積リストを取得
        Areas = sum([lst[2] for lst in freqNum_YList])
        #Y座標の期待値を算出
        Y_ = (int)(YAreas / Areas)
        #長方形を黒で塗りつぶす。
        img = cv2.rectangle(img, (leftX, 0), (rightX, Y_), (ret, ret, ret), -1)
        flag_image_modify_deeply = True
        if DEBUG:
            calculation_log(
                'img is filled with rectangle Xleft = {}, Xright = {}, Y = {}'.
                format(leftX, rightX, Y_))

        #塗りつぶし画像出力する処理
        if flag_export_fillImg:
            if not os.path.exists(os.path.dirname(path) + "/fillCnts"):
                os.makedirs(os.path.dirname(path) + "/fillCnts", exist_ok=True)
            savePath = (os.path.dirname(path) + '/fillCnts/'+ os.path.splitext(os.path.basename(path))[0] +\
                        '_fillCnts.jpg') if mkdir else (os.path.splitext(path)[0] + "_fillCnts.jpg")
            cv2.imwrite(savePath, img)

        #以下、二値化→輪郭抽出やり直し
        im = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        ret, im_bin = cv2.threshold(im, binarize_thresh, 255,
                                    (cv2.THRESH_BINARY_INV +
                                     cv2.THRESH_OTSU) if auto_binarize else
                                    (cv2.THRESH_BINARY_INV))
        #輪郭抽出
        contours, hierarchy = cv2.findContours(im_bin, cv2.RETR_EXTERNAL,
                                               cv2.CHAIN_APPROX_SIMPLE)
        #閾値でフィルタ
        contours = [
            cnt for cnt in contours if cv2.contourArea(cnt) > min_area_thresh
        ]

    #輪郭面積でソート
    contours.sort(key=lambda cnt: cv2.contourArea(cnt), reverse=True)
    if len(contours) > num_evaluate_contour_max:
        contours = contours[:num_evaluate_contour_max]
    yMax_at_largest_contour = max(contours[0], key=lambda pt2: pt2[0, 1])[0, 1]
    ymin_at_largest_contour = min(contours[0], key=lambda pt2: pt2[0, 1])[0, 1]
    calculation_log('ymax_at_largest_contour is {} after modified'.format(
        yMax_at_largest_contour))
    if yMax_at_largest_contour == imgYMax - 1:
        contours.pop(0)
        imgYMax = ymin_at_largest_contour
        calculation_log('delete bottom noise')
    if DEBUG:
        calculation_log('the length of contours is {}'.format(len(contours)))
    #輪郭結果格納リストの宣言
    cntResults = []

    #サテライトY順に降順ソート
    contours.sort(key=lambda cnt: min(cnt, key=lambda pt2: pt2[0, 1])[0, 1])

    #各輪郭に対する処理
    for cnt in contours:
        #輪郭面積
        area = cv2.contourArea(cnt)
        #周囲長
        perimeter = cv2.arcLength(cnt, True)
        #凸包輪郭計算
        hull = cv2.convexHull(cnt)
        #凸包面積
        convArea = cv2.contourArea(hull)
        #凸包周囲長さ
        convPerimeter = cv2.arcLength(hull, True)
        #モーメント計算
        M = cv2.moments(cnt)
        #重心X
        cx = int(M['m10'] / M['m00'])
        #重心Y
        cy = int(M['m01'] / M['m00'])

        #Y座標最大のpt左側にて
        yMax = max(cnt, key=lambda pt2: pt2[0, 1])[0, 1]
        pt_yMax_xLeft = [pt for pt in cnt if pt[0, 1] == yMax][0][0]
        pt_yMax_xRight = [pt for pt in cnt if pt[0, 1] == yMax][-1][0]
        main_X = (pt_yMax_xLeft[0] + pt_yMax_xRight[0]) / 2
        main_Y = pt_yMax_xRight[1]
        #Y座標最小のpt左側にて
        ymin = min(cnt, key=lambda pt2: pt2[0, 1])[0, 1]
        pt_yMin_xLeft = [pt for pt in cnt if pt[0, 1] == ymin][0][0]
        pt_yMin_xRight = [pt for pt in cnt if pt[0, 1] == ymin][-1][0]
        satellite_X = (pt_yMin_xLeft[0] + pt_yMin_xRight[0]) / 2
        satellite_Y = pt_yMin_xLeft[1]
        if DEBUG:
            calculation_log(
                'contour was analysed with main_y : {}'.format(main_Y))

        #体積と最頻Y座標を取得
        cntList = cnt.tolist()
        cntList.sort(key=lambda pt: pt[0][1])
        volume = 0.0
        freqNum_PtList = []
        for key, ptGroup in groupby(cntList, key=lambda pt: pt[0][1]):
            listPtGroup = list(ptGroup)
            freqNum_PtList.append([len(listPtGroup), key])
            if len(listPtGroup) > 0:
                x_min = min(listPtGroup, key=lambda pt2: pt2[0][0])[0][0]
                x_max = max(listPtGroup, key=lambda pt2: pt2[0][0])[0][0]
                volume = volume + ((x_max - x_min) / 2.0)**2 * math.pi

        freqNum_PtList.sort(key=lambda freqnum_pt: freqnum_pt[0])
        freq_num, freq_Y = freqNum_PtList[-1][0], freqNum_PtList[-1][1]
        if DEBUG:
            calculation_log(
                'contour was analysed with freq_Y : {}'.format(freq_Y))

        #解析結果の格納
        retResultTmp = Contour_Elem.Analysis_Result(
            delay=delay,
            main_X=main_X,
            main_Y=main_Y,
            satellite_X=satellite_X,
            satellite_Y=satellite_Y,
            area=area,
            arclength=perimeter,
            center_X=cx,
            center_Y=cy,
            convex_hull_contour_area=convArea,
            convex_hull_contour_arclength=convPerimeter,
            imgYMax=imgYMax,
            volume=volume,
            freq_num=freq_num,
            freq_Y=freq_Y,
            DEBUG=DEBUG)
        if DEBUG:
            calculation_log('cnt_class with main_Y is {} is generated'.format(
                retResultTmp.main_Y))
        #解析結果のリストへの追加
        cntResults.append(retResultTmp)
        if DEBUG:
            calculation_log('cnt with main_Y is {} is appended'.format(
                cntResults[-1].main_Y))

    cntResults = [cnt for cnt in cntResults if cnt.main_Y <= imgYMax]
    cntResults.sort(key=lambda res: res.satellite_Y)
    #面積で降順ソート
    analysis_contours = Analysis_Results_Class.Analysis_Results(
        delay, cntResults, DEBUG)
    if DEBUG:
        calculation_log('length of cntResults is {}'.format(len(cntResults)))
    #以下、画像出力対応
    if exportImage:
        #ディレクトリ作製
        if mkdir:
            if not os.path.exists(os.path.dirname(path) + "/drawRsts"):
                os.makedirs(os.path.dirname(path) + "/drawRsts", exist_ok=True)
                if DEBUG:
                    calculation_log('mkdir at {}/drawRsts'.format(
                        os.path.dirname(path)))
        #輪郭描画
        if draw_contour:
            #面積最大の輪郭のみ太線で描画
            img = cv2.drawContours(img, [contours[0]], -1, (0, 255, 0), 2)
            if len(contours) > 1:
                img = cv2.drawContours(img, contours[1:], -1, (0, 255, 0), 1)
        #凸包輪郭描画
        if draw_convexhull:
            #凸包輪郭取得
            hulls = [cv2.convexHull(cnt) for cnt in contours]
            #描画、面積最大のみ太線
            img = cv2.drawContours(img, [hulls[0]], -1, (255, 255, 0), 2)
            if len(hulls) > 1:
                img = cv2.drawContours(img, hulls[1:], -1, (255, 255, 0), 1)
        #座標描画
        if draw_coords:
            for i in range(len(cntResults)):
                main_X = cntResults[i].main_X
                main_Y = cntResults[i].main_Y
                img = cv2.rectangle(img,
                                    ((int)(main_X - 5), (int)(main_Y - 5)),
                                    ((int)(main_X + 5), (int)(main_Y + 5)),
                                    (0, 0, 255), 2 if i == 0 else 1)
                satellite_X = cntResults[i].satellite_X
                satellite_Y = cntResults[i].satellite_Y
                img = cv2.rectangle(img, ((int)(satellite_X - 5),
                                          (int)(satellite_Y - 5)),
                                    ((int)(satellite_X + 5),
                                     (int)(satellite_Y + 5)), (0, 255, 255),
                                    2 if i == 0 else 1)
                center_X = cntResults[i].center_X
                center_Y = cntResults[i].center_Y
                img = cv2.rectangle(img,
                                    ((int)(center_X - 5), (int)(center_Y - 5)),
                                    ((int)(center_X + 5), (int)(center_Y + 5)),
                                    (255, 255, 255), 2 if i == 0 else 1)
        #ファイルパス生成
        savePath = (os.path.dirname(path) + '/drawRsts/'+ os.path.splitext(os.path.basename(path))[0] + \
                    '_drawResult.jpg') if mkdir else (os.path.splitext(path)[0] + "_drawResult.jpg")
        #ファイル生成
        cv2.imwrite(savePath, img)
        if DEBUG:
            calculation_log('export image at {}'.format(savePath))

    #メモリ解放
    del img
    del im
    del contours
    gc.collect()
    #輪郭解析結果リストを返す
    return analysis_contours, flag_image_modify_deeply
    def __init__(self, analysis_results_list, area_mirgin_detect_faced,
                 area_mirgin_detect_separation,
                 arc_length_mirgin_detect_separation,
                 solidity_mirgin_detect_separation,
                 noise_remove_topological_dist_thresh,
                 noise_remove_area_thresh, DEBUG):
        '''
        コンストラクタ
        1:単純な輪郭データ群をディレイ時間でgroupByの後にリストとして格納
        2:ノイズを除去
        3:特徴量計算しクラス変数に格納        
        
        Parameters
        ----------
        analysis_results_list : [Analysis_Result]
            輪郭解析結果群
        area_mirgin_detect_faced : float
            液滴吐出開始検知閾値面積 [pix]
        area_mirgin_detect_separation : float
            液滴分離検知閾値面積[pix]
        arc_length_mirgin_detect_separation : float
            液滴分離検知閾値輪郭長[pix]
        solidity_mirgin_detect_separation : float
            液滴分離検知閾値solidity [U]
        noise_remove_topological_dist_thresh : float
            ノイズ除去時輪郭類似判定用の位相空間距離 [U]
        nosie_remove_area_thresh : int
            ノイズ除去時輪郭面積上限[pix]
        DEBUG : bool
            デバッグモード判定
        '''
        if DEBUG:
            calculation_log('constructor was called.')
        if DEBUG:
            calculation_log('set params to class_field parameters.')
        analysis_results_list = [
            rsts for rsts in analysis_results_list if len(rsts.contours) != 0
        ]
        #ディレイ時間にて昇順ソートしanalysisResultsに格納
        analysis_results_list.sort(key=lambda rst: rst.delay)
        self.analysisResults = analysis_results_list

        #以下、プロパティのセット
        self.__DEBUG = DEBUG
        self.__area_mirgin_detect_faced = area_mirgin_detect_faced
        self.__area_mirgin_detect_separation = area_mirgin_detect_separation
        self.__arc_length_mirgin_detect_separation = arc_length_mirgin_detect_separation
        self.__solidity_mirgin_detect_separation = solidity_mirgin_detect_separation
        self.__noise_remove_topological_dist_thresh = noise_remove_topological_dist_thresh
        self.__noise_remove_area_thresh = noise_remove_area_thresh
        if DEBUG:
            calculation_log(
                'set params to class_field parameters was completed.')
        #ノイズ除去関数によるノイズ除去
        self.__remove_noise()
        if DEBUG:
            calculation_log('noises was removed.')
        #パラメータセット関数によるパラメータセット
        self.__set_solidity()
        self.__set_droplet_faced()
        self.__set_droplet_separated()
        self.__set_regament_max()
        self.__set_arc_solidity()
        self.__set_volume()
        self.__set_freq_Y()
        self.__set_num_initial_contour()
        self.__set_magnitude_of_doublet_droplet()

        if DEBUG:
            calculation_log('set feature params was completed.')
        return None
Esempio n. 28
0
def auto_Tracking(
        dirPath,  #フォルダパス, str
        rsts,  #解析する輪郭群、analysis_result_list class
        draw_Tracking_Results,  #追尾結果の出力
        draw_Fitting_Line,  #フィッティング結果の描画,
        DEBUG  #デバッグモード指定
):
    """
    輪郭群抽出結果から液滴追尾結果を生成する関数
    
    Parameters
    ----------
    dirPath : str
        入力画像を格納するディレクトリパス。
    rsts : FindContourAnalysis.Analysis_Results_Listクラス
        解析対象の輪郭抽出結果群。
    draw_Tracking_Results : bool
        追尾結果の出力の有無、初期値False
    draw_Fitting_Line : bool
        フィッティング結果の描画、初期値False

    Returns
    -------
    tracking_Results : Tracking_Resultsクラス
        追尾結果。
    
    """

    if DEBUG:
        calculation_log('Auto_Tracking module was called.')
    #結果格納クラス宣言
    tracking_Results = Tracking_Results_Class.Tracking_Results()
    #輪郭格納データ配列のインデックス宣言
    #analysisResultsリストの2つめにて指定。
    index_delay = 0  #ディレイのインデックス
    index_Cnt = 1  #輪郭リストのインデックス

    #メイン液滴の追尾
    if rsts.flag_faced_is_detected == True:
        #追尾対象の抽出
        rstsMain = [
            rst for rst in rsts.analysisResults
            if rst.delay >= rsts.delay_faced
        ]
        #ディレイ順に昇順ソート
        rstsMain.sort(key=lambda rst: rst.delay)
        #検出開始時のメイン液滴座標のディレイと座標格納、面積最大の輪郭にて
        tracking_Results.Set_MainXY(rstsMain[0].delay,
                                    rstsMain[0].contours[0].main_X,
                                    rstsMain[0].contours[0].main_Y)
        #追尾対象のディレイ写真が2枚以上の場合
        if len(rstsMain) > 1:
            #追尾検出2枚目の検出
            if len(rstsMain[1].contours) > 1:
                #対象輪郭が2つ以上の場合、初回の結果との距離順にソート。ソート後、最近傍のメイン液滴を格納。
                rstsMain[1].contours.sort(
                    key=lambda rst:
                    (rst.main_X - tracking_Results.results[0].main_X)**2 +
                    (rst.main_Y - tracking_Results.results[0].main_Y)**2)
            #追尾検出2枚目のディレイと座標格納
            tracking_Results.Set_MainXY(rstsMain[1].delay,
                                        rstsMain[1].contours[0].main_X,
                                        rstsMain[1].contours[0].main_Y)
            #追尾検出3枚目以降
            for i in range(2, len(rstsMain)):
                #ソート基準座標の計算
                #ディレイ比計算
                delayRatio = (rstsMain[i].delay - rstsMain[i - 2].delay) / (
                    rstsMain[i - 1].delay - rstsMain[i - 2].delay)
                #予想基準座標算出
                ptEval_X = tracking_Results.results[-2].main_X + delayRatio * (
                    tracking_Results.results[-1].main_X -
                    tracking_Results.results[-2].main_X)
                ptEval_Y = tracking_Results.results[-2].main_Y + delayRatio * (
                    tracking_Results.results[-1].main_Y -
                    tracking_Results.results[-2].main_Y)
                #予想Y座標が画像Y座標最大値より大きい場合は追尾打ち切り
                if ptEval_Y >= rstsMain[i].contours[0].imgYMax:
                    if DEBUG:
                        calculation_log(
                            'main_tracking is stopped caused by the imgY_limitation at delay {}'
                            .format(rstsMain[i].delay))
                    break
                #評価輪郭数0の場合は打ち切り
                if len(rstsMain[i].contours) == 0:
                    if DEBUG:
                        calculation_log(
                            'main_tracking is stopped caused by zero evaluatable contours at delay {}'
                            .format(rstsMain[i].delay))
                    break
                #評価点が2点以上の場合の処理
                if len(rstsMain[i].contours) > 1:
                    #評価基準点と各輪郭のメイン液滴座標距離順に輪郭群をソート。ソート後、最近傍のメイン液滴を格納。
                    rstsMain[i].contours.sort(key=lambda rst: (
                        rst.main_X - ptEval_X)**2 + (rst.main_Y - ptEval_Y)**2)
                #最近傍座標が画像際下端より下に来た場合は打ち切り
                if rstsMain[i].contours[0].main_Y > rstsMain[i].contours[
                        0].imgYMax:
                    if DEBUG:
                        calculation_log(
                            'main_tracking is stopped caused by most_plausible main_Y over imgYMax at delay {}'
                            .format(rstsMain[i].delay))
                    break
                #最近傍座標が一つ前の結果より上に来た場合は打ち切り
                if rstsMain[i].contours[0].main_Y < tracking_Results.results[
                        -1].main_Y:
                    if DEBUG:
                        calculation_log(
                            'main_tracking is stopped caused by inverse_direction_main_Y at delay {}'
                            .format(rstsMain[i].delay))
                    break
                #検出結果の格納
                tracking_Results.Set_MainXY(rstsMain[i].delay,
                                            rstsMain[i].contours[0].main_X,
                                            rstsMain[i].contours[0].main_Y)

    if DEBUG:
        calculation_log('main_tracking was completed.')
    #サテライト液滴の追尾
    flag_sat_is_trackable = True
    if rsts.flag_separated_is_detected == True:
        #追尾対象の抽出
        rstsSatellite = [
            rst for rst in rsts.analysisResults
            if rst.delay >= rsts.delay_separated
        ]
        #ディレイ順に昇順ソート
        rstsSatellite.sort(key=lambda rst: rst.delay)
        #1枚目の輪郭数カウント、2以上でソート
        if len(rstsSatellite[0].contours) > 1:
            #面積順で輪郭リストをソート、降順
            rstsSatellite[0].contours.sort(key=lambda cnt: cnt.area,
                                           reverse=True)
            #対象輪郭の抽出、面積最大→ノズルの検出
            rstBase = rstsSatellite[0].contours[0]
            #satelliteのY座標がノズルのY座標より下に来るもののみ抽出
            rstAdd = [
                rst for rst in rstsSatellite[0].contours
                if rst.satellite_Y >= rstBase.main_Y
            ]
            if len(rstAdd) == 0:
                flag_sat_is_trackable = False
            if len(rstAdd) == 1:
                rstAdd
            else:
                #一つ前の結果からの距離順にて昇順ソート。
                rstAdd.sort(
                    key=lambda rst: (rst.satellite_Y - rstBase.main_Y)**2)

        if flag_sat_is_trackable:
            #1枚目輪郭の結果格納
            tracking_Results.Set_SatelliteXY(rstsSatellite[0].delay,
                                             rstAdd[0].satellite_X,
                                             rstAdd[0].satellite_Y)
            #追尾対象のディレイ写真が2枚以上の場合
            if len(rstsSatellite) > 1:
                #追尾検出2枚目の検出
                #2枚目に格納されている輪郭情報が2以上の場合
                if len(rstsSatellite[1].contours) > 1:
                    #サテライト液滴X座標がnanではないものを抽出。
                    lstSat = list(
                        filter(lambda rst: not math.isnan(rst.satellite_X),
                               tracking_Results.results))
                    #satelliteのY座標が一つ前のY座標より下に来るもののみ抽出
                    rstAdd = [
                        rst for rst in rstsSatellite[1].contours
                        if rst.satellite_Y >= lstSat[-1].satellite_Y
                    ]
                    print(len(rstAdd))
                    if len(rstAdd) > 0:
                        #一つ前の結果からの距離順にて昇順ソート。
                        rstAdd.sort(
                            key=lambda rst:
                            (rst.satellite_X - lstSat[-1].satellite_X)**2 +
                            (rst.satellite_Y - lstSat[-1].satellite_Y)**2)
                    else:
                        rstAdd = sorted(
                            lstSat,
                            key=lambda rst:
                            (rst.satellite_X - lstSat[-1].satellite_X)**2 +
                            (rst.satellite_Y - lstSat[-1].satellite_Y)**2)
                else:
                    #対象の輪郭が1つの場合、そのまま結果の格納用オブジェクトをコピー
                    rstAdd = rstsSatellite[1].contours
                #サテライト液滴追尾結果の格納
                tracking_Results.Set_SatelliteXY(rstsSatellite[1].delay,
                                                 rstAdd[0].satellite_X,
                                                 rstAdd[0].satellite_Y)
                print('2nd added')

                #追尾検出3枚目以降
                for i in range(2, len(rstsSatellite)):
                    #i枚目の輪郭格納結果数が0であれば打ち切り
                    if len(rstsSatellite[i].contours) == 0:
                        break

                    #すでに格納されている結果から、satellite_X座標がnanではないものを抽出。
                    lstSat = list(
                        filter(lambda rst: not math.isnan(rst.satellite_X),
                               tracking_Results.results))
                    #ひとつ前の結果のサテライトY座標より下にサテライトY座標がくるもののみを抽出
                    rstAdd = [
                        rst for rst in rstsSatellite[i].contours
                        if rst.satellite_Y >= lstSat[-1].satellite_Y
                    ]
                    #抽出結果数が0であれば打ち切り
                    if len(rstAdd) == 0:
                        break
                    #抽出結果を、ひとつ前のサテライト座標からの距離順に昇順ソート→最も近いものをサテライト液滴として採用。
                    rstAdd.sort(key=lambda rst:
                                (rst.satellite_X - lstSat[-1].satellite_X)**2 +
                                (rst.satellite_Y - lstSat[-1].satellite_Y)**2)
                    #サテライト検出結果を格納
                    tracking_Results.Set_SatelliteXY(rstsSatellite[i].delay,
                                                     rstAdd[0].satellite_X,
                                                     rstAdd[0].satellite_Y)

    if DEBUG:
        calculation_log('satellite_tracking was completed.')
        for elem in tracking_Results.results:
            calculation_log(
                'delay : {}us, main_X : {}, main_Y : {}, sat_X : {}, sat_Y : {}'
                .format(elem.delay, elem.main_X, elem.main_Y, elem.satellite_X,
                        elem.satellite_Y))

    #格納結果をディレイ時間に対して昇順でソート
    tracking_Results.results.sort(key=lambda rst: rst.delay)

    if DEBUG:
        calculation_log('results objects were sorted with rst.delay.')

    #追尾結果の描画指定の場合
    if draw_Tracking_Results:
        #追尾結果描画関数の呼び出し
        try:
            drawTrackingResult(dirPath=dirPath,
                               trackingRsts=tracking_Results,
                               draw_Fitting_Line=draw_Fitting_Line,
                               DEBUG=DEBUG)
        except:
            if DEBUG:
                calculation_log('auto_tracking_export_failure.')
    #指定したディレクトリパスをprint(!!!後ほどコメントアウト!!!)
    print(dirPath)
    #追尾結果を返す
    return tracking_Results
Esempio n. 29
0
def analyse_Images_List(directoryPath,
                        min_area_thresh,
                        binarize_thresh,
                        auto_binarize,
                        area_mirgin_detect_faced,
                        area_mirgin_detect_separation,
                        arc_length_mirgin_detect_separation,
                        solidity_mirgin_detect_separation,
                        noise_remove_topological_dist_thresh,
                        noise_remove_area_thresh,
                        mkdir,
                        draw_contour, 
                        draw_convexhull,
                        draw_coords,
                        exportImage,
                        draw_reg_Detected,
                        DEBUG):
    '''
    画像ディレクトリ→連続輪郭解析関数
    
    Parameters
    ----------------------
    directoryPath : str
        解析ディレクトリ名
    min_area_thresh : float
        最小面積閾値[pix]
    binarize_thresh : int
        二値化閾値
    auto_binarize : bool
        自動二値化(大津の方法)実行
    area_mirgin_detect_faced : float
        液滴吐出開始検知閾値[pix]
    area_mirgin_detect_separation : float
        液滴分離検知面積閾値[pix]
    arc_length_mirgin_detect_separation : float
        液滴分離検知輪郭長閾値[pix]
    solidity_mirgin_detect_separation : float
        液滴分離検知solidity比閾値[U]
    noise_remove_topological_dist_thresh : float
        ノイズ除去時輪郭類似判定位相空間距離閾値
    noise_remove_area_thresh : float
        ノイズ除去時輪郭面積上限[pix]
    mkdir : bool
        ディレクトリ作製フラグ
    draw_contour : bool
        輪郭描画フラグ
    draw_convexhull : bool
        凸包輪郭描画フラグ
    draw_coords : bool
        各座標描画フラグ
    exportImage : bool
        解析結果画像出力フラグ
    draw_reg_Detected : bool
        最大輪郭検出結果画像出力フラグ
    DEBUG : bool
        デバッグモードフラグ
    
    Returns
    ----------------------
    retAnalysisResults : Analysis_Results_Listクラス
        輪郭抽出結果群
    '''
    
    #ファイルリストの取得
    extension = '.jpg'
    files = glob.glob(directoryPath + "/*" + extension)
    if len(files) < 2:
        calculation_log('num_files are 0 or 1')
        return None
        
    if DEBUG:
        calculation_log('files are imported, {}'.format(len(files)))
        calculation_log('min_area_thresh is {}'.format(min_area_thresh))
    #解析結果集計用リスト
    analysisResultsList = []
    flag_image_modified_deeply = False
    #ファイル名末尾よりディレイ時間取得
    for filePath in files:
        #ファイル名前よりディレイ時間の取得
        delay = 0.0
        max_description_field_length = 10
        i = max_description_field_length
        flag_delay_is_get = False
        while i > len(extension):
            try:
                delay = float(filePath[-i:-len(extension)])
                flag_delay_is_get = True
                break
            except:
                i = i - 1
        
        if not flag_delay_is_get:
            delay = -1.0
        #輪郭抽出結果の格納

        if DEBUG:
            calculation_log('analyse_image method is calling for the delay {} with file {}'.format(delay, filePath))
        #画像→輪郭解析実行
        appendResults = analyse_Image(
            delay,
            filePath,
            min_area_thresh,
            binarize_thresh,
            auto_binarize,
            draw_contour,
            draw_convexhull,
            draw_coords,
            exportImage,
            mkdir,
            flag_export_fillImg = False,
            DEBUG = DEBUG)
        analysisResultsList.append(appendResults[0])
        if appendResults[1]:
            flag_image_modified_deeply = True
        if DEBUG:
            calculation_log('analysis image results were appended for the delay {} with file {}'.format(delay, filePath))

    #全画像輪郭解析実施後処理
    if DEBUG:
        calculation_log('contour_list was completed with length {}'.format(len(analysisResultsList)))
    #返り値格納用リストに再集計、コンストラクタにてデータ整理
    retAnalysisResults = Analysis_Contours_List_Class.Analysis_Results_List(
        analysis_results_list = analysisResultsList,
        area_mirgin_detect_faced = area_mirgin_detect_faced,
        area_mirgin_detect_separation = area_mirgin_detect_separation,
        arc_length_mirgin_detect_separation = arc_length_mirgin_detect_separation,
        solidity_mirgin_detect_separation = solidity_mirgin_detect_separation,
        noise_remove_topological_dist_thresh = noise_remove_topological_dist_thresh,
        noise_remove_area_thresh = noise_remove_area_thresh,
        DEBUG = DEBUG
    )
    #解析結果群格納クラス取得後処理    
    if DEBUG:
        calculation_log('retAnalysisResults object were generated, length is {}'.format(len(retAnalysisResults.analysisResults)))
    #最大リガメント長さの描画、フラグにて有無を制御
    if draw_reg_Detected:
        regamentResults = retAnalysisResults.max_regament_length, retAnalysisResults.delay_max_regament_detected
        if DEBUG:
            calculation_log('detected delay is {} us, and detected length is {} pix'.format(regamentResults[1], regamentResults[0]))
        if not math.isnan(regamentResults[1]):           
            #当該輪郭の検出
            drawResultCnts = list(filter(lambda data: data.delay == regamentResults[1], retAnalysisResults.analysisResults))[0]
            drawResultCnt = list(filter(lambda cnt: cnt.get_regament_length() == regamentResults[0], drawResultCnts.contours))[0]
            #ディレクトリ生成
            os.makedirs(directoryPath+'/rgResult', exist_ok=True)
            #当該ディレイの画像ファイルパス取得
            if DEBUG:
                calculation_log('mkdir at {}/rgResult'.format(directoryPath))
            try:
                filePath = glob.glob(directoryPath+'/*{0}.jpg'.format(str(drawResultCnts.delay)))[0]
            except:
                filePath = glob.glob(directoryPath+'/*{0}.jpg'.format(str((int)(drawResultCnts.delay))))[0]
            #当該画像データ取得
            im = cv2.imread(filePath)
            #リガメント描画
            im = cv2.line(im,
                          ((int)(drawResultCnt.main_X),(int)(drawResultCnt.main_Y)),
                          ((int)(drawResultCnt.satellite_X),(int)(drawResultCnt.satellite_Y)),
                          (255, 255, 128),
                          3)
            #ファイル出力
            savePath = directoryPath+'/rgResult/' + os.path.basename(filePath) + '_drawRegament.jpg'
            cv2.imwrite(savePath, im)
            if DEBUG:
                calculation_log('export regament result image, {}'.format(savePath))
        
        else:
            calculation_log('reg_detection is not succeed, so rgResult was not drawn.')        
    
    #成功処理
    if DEBUG:
        calculation_log('cnt selection was completed.')
    #関数終了
    return retAnalysisResults, flag_image_modified_deeply
Esempio n. 30
0
def gamma_function(img):
    calculation_log('gamma_function is calling.')
    return cv2.LUT(img, lookUpTable)