techno_memo

個人用の技術メモ。python・ROS・AI系のソフトウェア・ツールなどの情報を記載

ファイル命名規則/更新日/ファイルサイズなどを反映した探索機能

やりたいこと

 ファイル命名規則/更新日/ファイルサイズなどを反映したファイル探索機能を実装したい

  • ファイル命名規則の判定

  • ファイル作成・更新・アクセス日時・ファイルサイズの取得と条件判定

f:id:sd08419ttic:20190307213548p:plain

実装

ファイル命名規則の判定

フォルダ中に存在するファイル名に対して、ユーザーが指定した正規表現パターンにマッチするかを判定する。

正規表現ライブラリreのmatch関数を用いてファイル名を判定する。

    def check_filename_regexp(self,filefullpathlist,pattern=None):
        '''
        ファイル命名規則のチェック (正規表現)
        filefullpathlist:ファイルパスのリスト、pattern:正規表現パターン
        '''
        result_path_list = []

        for pathname in filefullpathlist:
            #ファイル名の抽出
            fname, ext = os.path.splitext( os.path.basename(pathname) )    #ファイル名/拡張子の取得

            if pattern is not None:
                result = re.match(pattern,fname)   #re.search:途中も含めた検索、re.match:先頭からの完全一致
            else:
                result = True                       #正規表現パターンを何も設定していないときは元のパスをすべて出力する
            if result:
                result_path_list.append(pathname)
        return result_path_list

よく使いそうなpatternの条件は下記のようなものがある

説明
^XX ファイル先頭がXXから始まる
[0-9]+ ファイル名がすべて数字
.*0 ファイル名に0を含む

その他、使用可能な条件は下記サイトなどが参考となる

qiita.com

ファイル作成・更新・アクセス日時の取得と条件判定

ファイル情報の取得はos.pathで提供される各種関数を使って取得する

datetime形式のファイル作成日時などはstrftimeで文字列変換して表示することができる。

下記にフルパスリストからファイル情報を取得してdataframe形式で保存するスクリプトを示す。

    def get_file_detai_info(self,filefullpathlist):
        '''
        各ファイルの詳細情報(タイムスタンプ、サイズ)情報を取得 (結果をDataFrame形式で返す)
        filefullpathlist:ファイルのフルパスリスト
        '''
        result_df = pd.DataFrame(np.zeros([len(filefullpathlist), 5]), columns=['path', 'create_date', 'access_date','update_date','size'])
        indx_num =0
        for pathname in filefullpathlist:
            #result_df['path'][indx_num] = pathname
            result_df.loc[indx_num,'path'] = pathname

            #作成日時
            create_time = os.path.getctime(pathname)
            create_time_dt = datetime.datetime.fromtimestamp(create_time)
            create_time_str = create_time_dt.strftime('%Y/%m/%d  %H:%M:%S')
            result_df.loc[indx_num,'create_date'] = create_time_str

            #アクセス日時の取得
            acces_time = os.path.getatime(pathname)
            acces_time_dt = datetime.datetime.fromtimestamp(acces_time)
            acces_time_str = acces_time_dt.strftime('%Y/%m/%d  %H:%M:%S')
            result_df.loc[indx_num,'access_date'] = acces_time_str

            #更新日時の取得
            update_time = os.path.getmtime(pathname)
            update_time_dt = datetime.datetime.fromtimestamp(update_time)
            update_time_str = update_time_dt.strftime('%Y/%m/%d  %H:%M:%S')
            result_df.loc[indx_num,'update_date'] = update_time_str

            #サイズの取得 (KB)
            file_size  = os.path.getsize(pathname)
            result_df.loc[indx_num,'size'] = file_size

            #出力結果の表示
            print(create_time_str,acces_time_str,acces_time_str,file_size)
            indx_num = indx_num + 1
        return result_df

日付を条件判定する場合はdatetime形式に変換して比較演算子を利用すると効率が良い

(ユーザーは文字列型のほうが入力しやすいため)

下記にファイル作成日時がユーザーが入力した日付以前か以後かを判定して条件が満たされればそのパスを返す処理を実装する。

    def check_file_create_date(self,filefullpathlist,mode="A", cdate = 0):
        '''
        各ファイルの作成日時が指定した日以前であるかをチェックする
        mode:A (検索日時以降に作成) :B (検索日時以前に作成))、cdate:日時 例:2018/06/06
        '''

        result_path_list = []
        conditon_dt = datetime.datetime.strptime(cdate, '%Y/%m/%d')

        for pathname in filefullpathlist:
            #作成日時
            create_time = os.path.getctime(pathname)
            create_time_dt = datetime.datetime.fromtimestamp(create_time)
            create_time_str = create_time_dt.strftime('%Y/%m/%d  %H:%M:%S')

            if mode == "A":   #ファイル作成日時の比較 (検索条件日時以前に作成)
                if conditon_dt < create_time_dt:
                    result_path_list.append(pathname)
            elif mode == "B": #ファイル作成日時の比較 (検索条件日時以後に作成)
                if conditon_dt > create_time_dt:
                    result_path_list.append(pathname)
        return result_path_list

その他、ファイルサイズ比較などの機能についても1クラスにして下記のように実装した。

github.com

GUIで検索条件を指定できるように作るといろいろな場面で使えるのでtkinterでの条件設定などをする機能を実装していく。

前処理① 画像に対する前処理(リサイズ/色補正など)

やりたいこと

 画像に対して下記のような前処理を実施する

  • リサイズ

  • 反転

  • トリミング/パディング

  • グレースケール変換/2値化/ノイズ除去

  • 色補正

 opencvではpandasのようなチートシートがあれば参考としたかったが、見つからなかったので下記のようにまとめてみた。  (ひとまず画像読み込みなどの基本機能から前処理まで。エッジ検出による特徴抽出、物体検出などは別途取り扱う)

f:id:sd08419ttic:20190302203240p:plain

実装

上記機能を1つのクラスにまとめて取り扱えるように実装した。

クラスのインスタンス生成時にファイルから画像を self.imgに取り込み、他関数呼び出しで変換後の画像を出力する仕組みとしている。

リサイズ処理

OpenCV機能を使い、サイズ直接入力・比率の両方で指定できるように実装

    def resize_img_aspect_ratio(self,ratio=1.0):
        '''
        画像を拡大縮小する (比率指定)
        '''
        if self.img is None:
            return None
        else:
            result = cv2.resize(self.img , (int(self.img.shape[0]*ratio), int(self.img.shape[1]* ratio)))
        return result

    def resize_img_pix_size(self,height = 200, width = 150):
        '''
        画像を拡大縮小する (ピクセルサイズ指定)
        '''
        if self.img is None:
            return None
        else:
            result = cv2.resize(self.img , (int(width), int(height)))
        return result

反転処理

横・縦・両方の軸をパラメータ指定して反転できるように実装した。

    def flip_img(self,mode="x"):
        '''
        画像を反転させる
        x: 横方向反転、y:縦方向反転 xy:両方向反転
        '''
        if self.img is None:
            return None
        else:
            if mode == "x": #X軸方向反転
                result = cv2.flip(self.img,1)
            elif mode == "y": #Y軸方向反転
                result = cv2.flip(self.img,0)
            elif mode == "xy": #両方向反転
                result = cv2.flip(self.img,-1)
            else:
                result = self.img
        return result

トリミング

比率ベースのトリミング/座標直接指定のトリミングの両方ができるように実装した。

    def trim_img_aspect_ratio(self,left=0.10, right = 0.10, up=0.10, down = 0.10):
        '''
        画像をトリミングする (左右上下それぞれでカットする部分の比率を0-1.0の間で指定)
        '''
        if self.img is None:
            return None
        else:
            left = max(min(0.5,left),0.0)
            right = max(min(0.5,right),0.0)
            up = max(min(0.5,up),0.0)
            down = max(min(0.5,down),0.0)
            left_edge = int(self.img.shape[0]*left)
            right_edge = int(self.img.shape[0] - self.img.shape[0]*right)
            up_edge = int(self.img.shape[1]*up)
            down_edge = int(self.img.shape[1] - self.img.shape[1]*down)
            result = self.img[up_edge:down_edge,left_edge:right_edge]
        return result

    def trim_img_pix_size(self,left_edge = 0, right_edge = 0,  up_edge = 0, down_edge = 0):
        '''
        画像をトリミングする (ピクセル指定。画像サイズ以上の場合は無視)
        left_edge,right_edge,up_edge,down_edge
        '''
        if self.img is None:
            return None
        else:
            left = max(min(self.img.shape[0],left_edge),0.0)
            right = max(min(self.img.shape[0],right_edge),0.0)
            up = max(min(self.img.shape[1],up_edge),0.0)
            down = max(min(self.img.shape[1],down_edge),0.0)
            result = self.img[up:down,left:right]
        return result

パディング

指定幅だけ上下左右同じ幅ゼロ埋めする関数とした。 (その他反転するパディングなども可能。opencv公式リファレンスを参照のこと)

    def zero_padding_img(self,pad_width = 10):
        '''
        画像の境界領域をゼロ埋めする
        pad_width:パディング幅サイズ
        '''
        if self.img is None:
            return None
        else:
            PAD_COL = [0,0,0]   #ゼロパディング
            result= cv2.copyMakeBorder(self.img,pad_width,pad_width,pad_width,pad_width,cv2.BORDER_CONSTANT,value=PAD_COL)
        return result

グレースケール・2値化処理

入力画像をグレースケース変換し、2値化アルゴリズムで0 or 255の2値に変換する処理を作成した。 2値化アルゴリズムはパラメータ切り替えで変更できる (デフォルトは大津の二値化)

    def get_gray_img(self,color_im= "None"):
        '''
        グレースケール画像を取得する (color_im:カラー画像データ(option))
        '''
        if color_im is "None":
            if self.img is None:
                return None
            else:
                return cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
        else:
            return cv2.cvtColor(color_im, cv2.COLOR_BGR2GRAY)

    def get_binary_img(self,prm="OTSU"):
        '''
        2値化処理 prm: "OTSU":大津の2値化、"BIN":静的な2値化、"ADAPT":ローカル閾値

        '''
        if self.img is None:
            return None

        gray_img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
        if prm == "OTSU":
            _, result = cv2.threshold(gray_img, 0, 255, cv2.THRESH_OTSU)
        elif prm == "BIN":
            _, result = cv2.threshold(gray_img, 128, 255, cv2.THRESH_BINARY)
        elif prm == "ADAPT":
            result = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 55, 20)
        else:
            result = self.img
        return result

モルフォジー変換によるノイズ除去

2値データに対して膨張・収縮・オープニング・クロージング (膨張と収縮を組み合わせる)ことでノイズを消去する手法を実装した。

(手書き文字認識などのスキャンデータ、ROSの障害物地図データなどの処理などに利用できる)

パラメータは公式のままなので画像サイズや特性に応じて設定が必要

    def get_morphology_img(self,prm="Ero",binimg="None"):
        '''
        モルフォロジー変換 prm: "Ero":収縮、"Dil":膨張、"Ope":オープニング(膨張+縮小) "Clo":"クロージング(縮小+膨張)"

        '''
        if binimg is "None":
            if self.img is None:
                return None
            gray_img = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
            _, bin_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_OTSU)
        else:
            bin_img = binimg

        kernel = np.ones((3,3),np.uint8)

        if prm == "Ero":
            result = cv2.erode(bin_img, kernel,iterations = 2)
        elif prm == "Dil":
            result = cv2.dilate(bin_img, kernel,iterations = 2)
        elif prm == "Ope":
            result = cv2.morphologyEx(bin_img, cv2.MORPH_OPEN, kernel)
        elif prm == "Clo":
            result = cv2.morphologyEx(bin_img, cv2.MORPH_CLOSE, kernel)
        else:
            result = self.img
        return result

色補正フィルタ

画像の色を補正するためガンマ補正(色濃淡・明るさ)、ガウシアンフィルタ(ジャギー低減)、鮮鋭化フィルタ(エッジ強調)を実装した。

あくまで一般的な例なので、パラメータや他フィルタについても必要に応じて実装する必要がある。

    def get_gamma_img(self,gamma=1.0):
        '''
        ガンマ補正変換 (濃淡の補正)
        gamma: 1.0がデフォルト 小さくするほど薄く白っぽく、高くするほど濃く黒っぽい画像になる

        '''
        if self.img is None:
            return None
        LTB = np.empty((1,256), np.uint8)
        for i in range(256):
            LTB[0,i] = np.clip(pow( float(i) / 255.0, gamma) * 255.0, 0, 255)   #Look Up Tableの生成
 
        result = cv2.LUT(self.img, LTB) #輝度値のガンマ補正
        return result

    def get_gausian_filter_img(self,filtsize=5):
        '''
        ガウシアンフィルタ画像:ぼかしフィルタ (ジャギーなどの低減に有効)
        filtsize:フィルタサイズ(整数型かつ奇数)
        '''
        if self.img is None:
            return None
        # Look up tableを使って画像の輝度値を変更
        result = cv2.GaussianBlur(self.img,(filtsize,filtsize),0)
        return result


    def get_sharp_filter_img(self):
        '''
        鮮鋭化フィルタ画像:ボケた画像のエッジ・テクスチャ協調に有効
        '''
        if self.img is None:
            return None
        kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]], np.float32)
        result = cv2.filter2D(self.img, -1, kernel)
        return result

ソースコードは下記で公開している。

github.com

参考サイト

labs.eecs.tottori-u.ac.jp

labs.eecs.tottori-u.ac.jp

labs.eecs.tottori-u.ac.jp

Webサイトからのデータ取得 ①Google系Webサービスのデータ取得(サイト検索・画像検索・Youtube検索)

やりたいこと

 pythonで下記のGoogleWebサービスの結果を自動取得する

  • Gooogle検索 (キーワードにヒットした検索結果のタイトル、リンク一覧)

  • Google画像検索 (キーワードにヒットした検索結果の画像ファイル)

  • Youtube動画検索 (キーワードにヒットした検索結果の動画ファイル)

f:id:sd08419ttic:20190227004611p:plain

Google検索(キーワードにヒットした検索結果のタイトル、リンク一覧)

実装手順

①requestsライブラリでgoogleに検索を要求し、結果を取得

②Beautifulsoupライブラリでタイトル・リンクを抽出

③日本語エンコード変換に注意しつつ結果をpandas dataframeに保存

※検索結果の説明文も取得することは可能だが、タイトルと紐づけて取得するのが難しかった (リンクだけ表示されるものがある)ので省略

    def Get_GoogleWebSearch_Result(self,keyword,num=100):
        '''
        GoogleWeb検索結果と説明文の取得
        '''
        result_df = pd.DataFrame(columns=['Title','Link'] )
        title_list = []
        link_list = []

        print("Loading...")
        res = requests.get("https://google.com/search?num="+str(num)+"&q=" + " ".join(keyword))
        res.encoding = res.apparent_encoding
        res.raise_for_status()
 
        # 上位の検索結果のリンクを取得する
        soup = BeautifulSoup(res.content, "lxml")
        link_elems = soup.select('.r > a')         #タイトル・リンク
        link_elems2 = soup.select('.s > .st')      #説明文 (タイトル・リンクと一緒に表示させたかったがずれるので取得のみ)
 
        # 各結果をブラウザのタブで開く
        num_open = min(100, len(link_elems2))
        for i in range(num_open):
             tex_temp = link_elems[i].get_text()    #タイトル文字列の処理
             try:
                print(tex_temp)
             except:    #機種依存文字を含む場合はエンコード可能な文字だけを出力
                tex_temp = tex_temp.encode('cp932',"ignore")
                tex_temp = tex_temp.decode('cp932')
                print(tex_temp)

             link_temp = "https://google.com" + link_elems[i].get("href")
        
             #結果への追加
             title_list.append(tex_temp)
             link_list.append(link_temp)
             #tex_temp2 = link_elems2[i].get_text() #説明文の保存 (タイトル・リンクと一緒に表示させたかったがずれるので取得のみ)
             #try:
             #   print(tex_temp2)
             #except:    #機種依存文字を含む場合はエンコード可能な文字だけを出力
             #   tex_temp2 = tex_temp2.encode('cp932',"ignore")
             #   tex_temp2 = tex_temp2.decode('cp932')
             #   print(tex_temp2)
             #webbrowser.open("https://google.com" + link_elems[i].get("href"))

        result_df['Title'] = pd.Series(title_list)
        result_df['Link'] = pd.Series(link_list)

        return result_df

Google画像検索 (キーワードにヒットした検索結果の画像ファイル)

実装手順

①画像保存先ディレクトリの存在を確認し、すでにある場合はツリーごと削除(自動でリネーム保存されるため)

google_images_downloadライブラリで画像検索結果をフォルダに保存

    def Get_GoogleImageSearch_Result(self,keyword,imnum=100):
        '''
        Google画像検索結果の取得 (取得した画像は実行ファイルと同じ階層のdownloadフォルダに検索ワードのフォルダを作って保存)
        '''
        imnum = int(min(99,imnum))   #100以上の場合はchrome driverの設定が必要
        target_dir =os.path.dirname(os.path.abspath(__file__))+"/downloads/"+keyword
        if os.path.exists(target_dir):
            shutil.rmtree(target_dir)

        response = google_images_download.googleimagesdownload()
        arguments = {"keywords":keyword,"limit":imnum,"la":"Japanese","pr":"test"}   #creating list of arguments
        response.download(arguments)
        return

Youtube動画検索 (キーワードにヒットした検索結果の動画ファイル)

実装手順

①requestsライブラリでYoutubeに検索を要求し、結果を取得

②Beautifulsoupライブラリでタイトル・リンクを抽出

※①②については下記記載の参考リンクにある処理を転用させていただきました

③pytubeライブラリで取得したリンクから動画をmp4形式でダウンロード

※動画形式によっては失敗する場合がある。成功したものだけを取得する。

    def Get_YoutubeSearch_Result(self,keyword,imnum=20):
        '''
        Youtube検索結果の取得 (取得した画像は実行ファイルと同じ階層のmovieフォルダに検索ワードのフォルダを作って保存)
        '''
        imnum = int(min(20,imnum))
        contents = list(Youtube.search(keyword, num=imnum))

        target_dir =os.path.dirname(os.path.abspath(__file__))+'/movie/'+keyword+"/"
        if os.path.exists(target_dir):
            shutil.rmtree(target_dir)
        os.makedirs(target_dir)

        for indx in range(len(contents)):
            tex_title = contents[indx].title.encode('cp932',"ignore")
            tex_title = tex_title.decode('cp932')
            print(tex_title)
            try:
                yt = YouTube(contents[indx].url)
                stream = yt.streams.get_by_itag(22) #MP4動画(720p)は22/ MP4音楽:140 失敗する動画はyt.streams.all()で表示後個別調整すれば取得可能
                stream.download(target_dir)
            except:
                print("error")
        return

実装済ソースコード

github.com

参考サイト

Google検索結果のスクレイピング

su-gi-rx.com

Google画像検索の取得ライブラリ Google-images-download

github.com

Youtube検索結果のスクレイピング方法

qiita.com

Youtube動画ダウンロードライブラリ Pytube

github.com

データマージ方法① 複数ファイルに記載された集計データに対するマージ

やりたいこと

 複数ファイルに記載された集計データのマージ   * 各種の集計結果 (アンケート、帳票など)

* 時系列データ (タイムスタンプの処理)については別記事で記載予定

複数ファイルに記載された集計データのマージ

 一般的によく使われそうなケースを想定して場合分けして考える

①複数グループのデータのマージ

  • 重複する対象がない複数グループに対して同じ項目の計測をしたデータ

(下記では複数のチームに対する身体測定結果の例でイメージを記載)

[f:id:sd08419ttic:20190224192344p:plain]

実装

pandasのconcat関数を適用する

pandasのデータフレームリスト(以前記載した方法で取得)を引数とすると下記の処理でマージできる。

    def merge_data_simlpe(self,input_pd_list):
        '''
        最もシンプルなデータのマージ (pandas concatを使った縦方向の結合)
        '''
        result = input_pd_list[0]
        for dnameindx in range(len(input_pd_list)-1) :
            result = pd.concat([result,input_pd_list[dnameindx+1]])

        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result

②同一対象を含むデータのマージ

  • 重複する対象を含む複数ファイルのデータ (同じ対象を別の時間で計測した結果など)

(下記では同じ組織に対する年度毎の身体測定結果の例でイメージを記載)

f:id:sd08419ttic:20190224204859p:plain

実装①

各dataframeに新しい列を追加し、ファイル名の情報を追加する

追加したdfをpandasのconcat関数を適用して結合する

    def merge_data_with_filenamecol(self,input_pd_list,fname_list):
        '''
        ファイル名を新しい列として追加するマージ
        '''
        result = input_pd_list[0]

        fname, ext = os.path.splitext( os.path.basename(fname_list[0]) )    #ファイル名/拡張子の取得
        result["file_name"] = fname
        for dnameindx in range(len(input_pd_list)-1) :
            fname, ext = os.path.splitext( os.path.basename(fname_list[dnameindx+1]) )    #ファイル名/拡張子の取得
            temp_df = input_pd_list[dnameindx+1]
            temp_df["file_name"] = fname
            result = pd.concat([result,temp_df])

        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result
実装②

各dataframeの列にファイル名を添え字として保存する (下記の例)

f:id:sd08419ttic:20190224220712p:plain

上図において全て存在しているものを残す場合と、存在しない場合はでNanとする場合の2つの実装を考慮する。

(この2つはデータの種類によってどちらの形式を選択するか考慮したほうが良いと考えたため)

    def merge_data_with_merge_only_common(self,input_pd_list,fname_list):
        '''
        一番左の列の要素をキーとして各ファイルのデータを添え字付きでマージする処理 (全てのファイルにキーが存在する行のみ表示)
        '''
        result = input_pd_list[0]
        fname, ext = os.path.splitext( os.path.basename(fname_list[0]) )    #ファイル名/拡張子の取得
        ori_columns = result.columns.tolist()
        for colindx in range(len(ori_columns)-1):
            ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
        result.columns = ori_columns
        key_name = result.columns[0]    #一番左の列をキーとする

        for dnameindx in range(len(input_pd_list)-1) :
            fname, ext = os.path.splitext( os.path.basename(fname_list[dnameindx+1]) )    #ファイル名/拡張子の取得
            temp_df = input_pd_list[dnameindx+1]
            ori_columns = temp_df.columns.tolist()
            for colindx in range(len(ori_columns)-1):
                ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
            temp_df.columns = ori_columns
            result = pd.merge(result,temp_df,on=key_name)
        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result

    def merge_data_with_merge_all_data(self,input_pd_list,fname_list):
        '''
        一番左の列の要素をキーとして各ファイルのデータを添え字付きでマージする処理 (キーが不在の場合はNanで表示)
        '''
        result = input_pd_list[0]
        fname, ext = os.path.splitext( os.path.basename(fname_list[0]) )    #ファイル名/拡張子の取得
        ori_columns = result.columns.tolist()
        for colindx in range(len(ori_columns)-1):
            ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
        result.columns = ori_columns
        key_name = result.columns[0]    #一番左の列をキーとする

        for dnameindx in range(len(input_pd_list)-1) :
            fname, ext = os.path.splitext( os.path.basename(fname_list[dnameindx+1]) )    #ファイル名/拡張子の取得
            temp_df = input_pd_list[dnameindx+1]
            ori_columns = temp_df.columns.tolist()
            for colindx in range(len(ori_columns)-1):
                ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
            temp_df.columns = ori_columns
            result = pd.merge(result,temp_df,on=key_name, how='outer')
            print(result)

        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result

実装結果は下記に記載

github.com

その他集約するファイル名の命名ルールで判定する、データ種別で結合可能か判定するなどのアプローチがあるが長くなりそうなので別記事で記載する。

画像・動画ファイル読み込み方法 (フォルダ配下の画像/動画/ドキュメントを一括読み込み)

やりたいこと

 ユーザーが指定したローカルフォルダにある下記ファイルを画像形式で読み込む

  • jpgファイル

  • pngファイル

  • aviファイル

  • mp4ファイル

  • pdf ファイル (各ページを画像として取得)

 フォルダ・ファイルパスを指定すると拡張子からフォーマットを識別して取得

f:id:sd08419ttic:20190220235434p:plain

 動画は全フレーム/PDFは全ページを読み込む

 読み込み結果はpandas dataframeのリスト(ファイル数分)、ファイル名のリストとして出力する

事前準備

 pdfファイルの読み込みにpdf2imageというライブラリを利用する

github.com

 上記ライブラリはpopperというアプリを動かす必要があるため、下記サイトからダウンロードして保存する

blog.alivate.com.au

 解凍後にbinファイルのパスを環境変数に指定する必要がある。  下記githubスクリプトではpythonファイル内でパスを指定する処理を入れているのでフォルダ名を変更すれば利用できる。

実装

実装結果

https://github.com/sd08419ttic/python_tech_memo

各フォーマットファイルをnumpy ndarray として読み込む

画像・動画はopencvの関数を利用する (動画はフレームを1フレームづつ読み込み、成功した場合は保持する)

pdfはPILファイルをndarrayに変換した後で、色の順序をBGRにして保存する

    def read_image_data(self,filename):
        '''
        画像データを読みとる (jpg/png/bmp)
        '''
        result = cv2.imread(filename)
        return result

    def read_movie_data(self,filename):
        '''
        動画ファイルを読み込む(avi,mp4,wmv)
        '''
        result_list = []
        try:
            cap = cv2.VideoCapture(filename)
            ret, frame = cap.read()             #最初のフレームを取得
        except:
            print(filename ,"could not loaded!")
            traceback.print_exc()
            return result_list

        indx = 0
        while (cap.isOpened()):
            ret, frame = cap.read()             #最初のフレームを取得
            if ret == True: #読み込み成功判定
                #img = cv2.cvtColor(frame, cv2.COLOR_BGR)
                result_list.append(frame)
                print("deb_movie",indx)
                indx = indx +1
            else:
                break
        cap.release()
        return result_list

    def read_pdf_page(self,filename):
        '''
        PDFファイルを画像として読み込む (ページを一括変換)
        '''
        result_list = []
        try:
            images = convert_from_path(filename)
            for indx in range(len(images)):
                img_temp = np.asarray(images[indx])
                img_temp = img_temp[:, :, ::-1].copy()    #RGB->BGR
                result_list.append(img_temp)
        except:
            print(filename, "could not loaded!")
            traceback.print_exc()
            result_list = null
        return result_list

参考サイト・書籍

haitenaipants.hatenablog.com

参考書 Pythonではじめるデータラングリング ―データの入手、準備、分析、プレゼンテーション

www.amazon.co.jp

トップページ

このブログのコンセプト

業務等で活用しやすい『機能単位』でノウハウをまとめる

  • イラスト1つでやりたいことがわかる構成とする

  • 一般的なPCの開発環境(Windows+Anaconda等)ですぐに試せるコードをgithubに残す

  • 詳細な機能の記事(各種ライブラリに関する使い方説明など) は他サイトのリンクにとどめる

カテゴリ

pythonによるデータ分析

  • 業務等でpythonスクリプトを用いたデータ処理・業務改善を行うことを想定した場合のノウハウまとめ

f:id:sd08419ttic:20190321220444p:plain

データ読み込み

題目 説明 リンク
数値データの読み込み 指定したフォルダの各種ファイル(csv/xlsx/json等)から数値データを読み込む リンク
画像・動画データの
読み込み
指定したフォルダの画像・動画ファイル(jpg/png/avi等)から数値データを読み込む リンク
Webサイトからの
データ取得
WEBサイトから情報を取得する リンク
命名規則・作成日などの
条件付きファイル探索
条件にマッチしたファイルだけを探索して取得する リンク

前処理

題目 説明 リンク
画像データの前処理 画像データを処理しやすいように整える(サイズ・ノイズ除去など) リンク
数値データの前処理 数値データのノイズ・欠損値を処理する リンク
数値データマージ・削減 データの内容に応じてマージ・削減する処理 リンク

特徴抽出/パターン認識

題目 説明 リンク
物体検出① 簡易的な物体検出を行う(テンプレートマッチング/色検出/エッジ検出) リンク
物体検出② YoloV3を用いたWebカメラ画像の物体検出 リンク
統計処理 入力データの特性を算出して可視化する TBD
機械学習 データの特性を学習し、適切に分類する TBD

GUI出力/ユーザー要求処理

題目 説明 リンク
GUI tkinterを用いたGUI実装 リンク
グラフ表示 matplotlibを用いたデータの可視化 リンク
グラフ表示(アニメーション) matplotlibのアニメーション描画 リンク
exe化 pyinstllerを用いたpythonスクリプトの実行ファイル化 リンク
ユーザー入力の受理 pygameを用いたキーボード/ゲームパッド操作取得 リンク

電子工作プロトタイピング

f:id:sd08419ttic:20190221233311p:plain

前提知識

題目 説明 リンク
開発環境 プロトタイプ用ハード・開発環境に関する考え方 リンク
Raspberry Pi開発環境(ハード) 必要なハードウェアについて リンク
Raspberry Pi開発環境(ソフト) Ubuntu mate/ROSの導入方法について リンク
ソフトウェアアーキテクチャ プロトタイピング時のソフトウェアアーキテクチャについて リンク

環境構築

題目 説明 リンク
Pythonソフト構造 複数機能・ファイルから成り立つプロジェクトの構築 リンク
Python処理タイミング 各機能の動作タイミングの設計方法 TBD
Arduinoソフト構造 複数機能・ファイルから成り立つプロジェクトの構築 リンク
Arduino処理タイミング 各機能の動作タイミングの設計方法 リンク

通信機能

題目 説明 リンク
ArduinoとPCの通信 シリアル通信(USBケーブル) リンク
PCとの接続 リモートデスクトップ/カメラ画像の送信/信号の送信 TBD
クラウドサービスとの接続 IFTTT連携 (Google Home/スマートフォン) TBD

ROS開発

題目 説明 リンク
ROS基礎 概念/用語/コマンドの使い方 リンク
ROS基礎2 プロジェクト構築 リンク
PCとの通信 Ubuntu PCとRaspberry piとの接続/制御信号の通信 リンク
ロボットモデルの記述 URDFファイルにロボットのモデルを記述する リンク
移動ロボット基礎 シミュレーション上でロボットを移動させる リンク
移動ロボットの走行経路 ロボットの走行経路を設定する リンク
移動ロボットの経路追従 走行経路を追従する車速・角速度の制御 リンク

センシング

題目 説明 リンク
カメラセンシング Raspberry pi カメラを使った画像処理 リンク
各種センサ実装 各種センサの実装 TBD

pandasのデータファイル読み込み方法 (フォルダ配下のファイルを一括読み込み)

やりたいこと

 Pandasでユーザーが指定したローカルフォルダにある下記データファイルを読み込む

  • csvファイル

  • tsvファイル

  • xls/xlsxファイル

  • jsonファイル

 フォルダ・ファイルパスを指定すると拡張子からフォーマットを識別して取得

f:id:sd08419ttic:20190219221412p:plain

 各データファイルは1行目に項目ラベル、2行目以降にデータが記述されていることが前提

 読み込み結果はpandas dataframeのリスト(ファイル数分)、ファイル名のリストとして出力する

実装

実装結果

github.com

各フォーマットファイルをpandas data frame として読み込む

pandasの各種フォーマットの読み込み関数を利用する 入力パスの拡張子からそれぞれの関数を呼び分ける

#csvデータの読み込み
    def read_file_autohandle(self,input_path):
        '''
        入力ファイルから拡張子を自動判定してpandasに読み込む
        '''
        root, ext = os.path.splitext(input_path)  #拡張子の取得

        #拡張子毎に読み込みを実施
        if ext == ".csv":
            result = self.read_csv_data(input_path)
        elif ext == ".tsv":
            result = self.read_tsv_data(input_path)
        elif (ext == ".xls") or (ext == ".xlsx"):
            result = self.read_xls_data_firstseat(input_path)
        elif ext == ".json":
            result = self.read_json_data(input_path)
        return result


    def read_csv_data(self,filename):
        '''
        csvデータを読みとる
        '''
        result = pd.read_csv(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_tsv_data(self,filename):
        '''
        tsvデータを読みとる
        '''
        result = pd.read_table(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_xls_data_firstseat(self,filename):
        '''
        xlsx or xlsファイルの1枚目のシートを読みとる
        '''
        result = pd.read_excel(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_xls_data_allsheat(self,filename):
        '''
        xlsx or xlsファイルの全てのシートを読み込む
        '''
        result = pd.read_excel(filename, encoding="shift-jis", sheetname =None)    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_json_data(self,filename):
        '''
        jsonファイルを読み込む
        '''
        result = pd.read_json(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

フォルダ内のファイルリストを探索する

フォルダ内のファイルリストを作成し、指定した拡張子に該当するものだけを抽出する サブフォルダ含めたファイルリストの作成はos.walkを用いる

    def __init__(self, ext="ALL"):
        '''
        コンストラクタ
        (読み込み対象とする拡張子を引数extで指定可能)
        '''
        if ext !="ALL":
            self.target_ext = [ ext ]
        else:
            self.target_ext = [".csv",".tsv",".xls",".xlsx",".json"]
        return

    def get_file_path_list_in_folder(self,folder_path):
        '''
        入力フォルダパス直下にあるファイルのリストを作成する
        '''
        if folder_path[-1] != "\\":
            folder_path = folder_path + "\\"


        file_full_path_list = []
        # Search_Current_Directry
        for file in os.listdir(folder_path):
            root, ext = os.path.splitext(file)
            for indx2 in range(len(self.target_ext)):
                if ext == self.target_ext[indx2]:
                    file_fullpath = folder_path + file
                    file_full_path_list.append(file_fullpath)
        return file_full_path_list

    def get_file_path_list_in_folder_recrussive(self,folder_path):
        '''
        入力フォルダパスの配下(サブフォルダ含む)にあるファイルのリストを作成する
        '''
        if folder_path[-1] != "\\":
            folder_path = folder_path + "\\"
        file_full_path_list = []

        for folder, subfolders, files in os.walk(folder_path):
            for indx in range(len(files)):
                root, ext = os.path.splitext(files[indx])
                for indx2 in range (len(self.target_ext)):
                    if ext == self.target_ext[indx2]:
                        file_fullpath = folder+'/'+ files[indx]
                        file_full_path_list.append(file_fullpath)
        return file_full_path_list

参考サイト・書籍

pandasへのデータ読み込み yolo.love

参考書 Pythonではじめるデータラングリング ―データの入手、準備、分析、プレゼンテーション

www.amazon.co.jp