月別アーカイブ: 2021年12月

Seleniumでスクレイピングの準備

$sudo apt-get update
$sudo apt install chromium-chromedriver
$sddo cp /usr/lib/chromium-browser/chromedriver /usr/bin
$pip install selenium
$pip install webdriver_manager

紛らわしい点:webdriver_managerとwebdrivermanagerの両方が存在し、機能が同じではない。webdriver_managerの方が良さそう。

Webサイトのタイトルを取得してみる。

from selenium import webdriver
import time

#---------------------------------------------------------------------------------------
# 処理開始
#---------------------------------------------------------------------------------------
# ブラウザをheadlessモード実行
print("\nブラウザを設定")
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome('chromedriver',options=options)
driver.implicitly_wait(10)

# サイトにアクセス
print("サイトにアクセス開始")
URL="https://rfsec.ddns.net/db/"
driver.get(URL)
time.sleep(3)
# driver.find_elements_by_css_selector("xxx") 的な処理を自由に
print("サイトのタイトル:", driver.title)

認証があるサイトの場合(中華製ネットワークカメラ)

import time
import base64
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

def get_auth_header(user, password):
    b64 = "Basic " + base64.b64encode('{}:{}'.format(user, password).encode('utf-8')).decode('utf-8')
    return {"Authorization": b64}

# Webdriver ManagerでChromeDriverを取得
driver = webdriver.Chrome(executable_path=ChromeDriverManager().install())

# Authorizationヘッダを付与
driver.execute_cdp_cmd("Network.enable", {})
driver.execute_cdp_cmd("Network.setExtraHTTPHeaders", {"headers": get_auth_header("admin", "")})
# Basic認証が必要なページにアクセス
driver.get('http://192.168.68.128')
time.sleep(5)

driver.close()
driver.quit()

数独の問題サイトから問題を取得して、解く。

#  ここからがseleniumのコード
#  問題サイト http://numberplace.net/
#
from selenium import webdriver
import time
import numpy as np

def disp(results):
    msg=""
    for r in results:
        for y in range(9):
            for x in range(9):
                c = r._values[y][x]
                c = str(c)
                d = row2[y][x]
                if d != 0:
                    msg=msg+'('+ c + ') '
                else:
                    msg=msg+'-'+ c + '- ' 
            msg=msg+"\n"
    print(msg)

#---------------------------------------------------------------------------------------
# 処理開始
#---------------------------------------------------------------------------------------
# ブラウザをheadlessモード実行
print("\nブラウザを設定")
options = webdriver.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
driver = webdriver.Chrome('chromedriver',options=options)
driver.implicitly_wait(2)

# サイトにアクセス
for num in range(5):
    URL="http://numberplace.net/?no="+str(num+1)
    print("サイトにアクセス開始:",URL)
    driver.get(URL)
    time.sleep(2)
    lines= driver.page_source.splitlines()
    for line in lines:
        if 'toi' in line:
            q = line.split(' ')[3].replace("'","").replace(";","")
            q=list(q)
            #print(q)
            qi = [int(s) for s in q]
            #print(qi)
            q2 = np.array(qi)
            row2=np.array(q2).reshape(-1,9).tolist()
            grid = solver.Grid(row2)
            print(grid)
            results = solver.solve_all(grid)
            disp(results)
            break
print('Done.')

OCRをマルチプロセスで処理

参考にしたサイト

multiprocessing — プロセスベースの並列処理 を参考に試す

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))

時間がかかっているOCR処理のコードを抜きだして関数化。一文字の画像を入力(peace)として、文字認識を行い、結果の数字を返す。高速化のために、事前に数字が入っていないと思われる画像については、OCRせずに、ゼロを返す。また、OCRの結果、数字以外として認識した場合も、ゼロを返す。

def ocr(peace):
    global p_max
# 画像イメージの総和が事前に計算したp_maxより小さい場合にOCRを呼び出す
    if np.array(peace).sum()<p_max: 
        conf='-l eng --psm 6  outputbase digits'
        txt=pytesseract.image_to_string(peace, config=conf)
        txt=remove_control_characters(txt)
        if txt.isdigit():
            ret=int(txt)
        else:
            ret=0
    else:
        ret=0
    return ret

multiprocess導入前のコード

画像81個(=9×9)をfor ループで一枚づつOCRを実行していた。peacesは画像81個のデータ。peaceは画像1個のデータ

my_bar = st.progress(0) # 時間の経過がわかるようにプログレスバーを設定
for peace in peaces:
        t = p_size[n]
        if t<p_max:
            txt=pytesseract.image_to_string(peace, config=conf)
            txt=remove_control_characters(txt)
            try:
                 ans=int(txt)
            except:
                 ans=0
               st.write('Error at:',n,'(',txt,')')
            else:
                 ans=0
            row.append(ans)
            my_bar.progress(int(100*n/80))
            n=n+1

row2=np.array(row).reshape(-1,9).tolist() # 結果を 9x9の形に
st.success(row2)

multiprocess導入後のコード

with Pool(int(CORE)) as p: # COREはセレクターで選んだ数 1,2,4,8,10,12,16,32
     ans=(p.map(ocr, peaces))

row.append(ans)
row2=np.array(row).reshape(-1,9).tolist()
st.success(row2)

結果 Pi4 Ubuntu20.04

$ uname -a
Linux ubuntu 5.4.0-1047-raspi #52-Ubuntu SMP PREEMPT Wed Nov 24 08:16:38 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux
------------------------------
Pool 経過時間
1	  17.721517086029053
2	  12.919192552566528
4	  12.035958051681519
8	  10.886781454086304
12	 10.22145414352417
16	 10.279670715332031
32	 11.4353609085083

rock pi(6 core)

$ uname -a
Linux rock 4.4.154-110-rockchip-gcef30e88a9f5 #1 SMP Mon Jun 22 07:37:10 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux
------------------------------
Pool 経過時間
1	  10.592926979064941
2	  7.96256422996521
4	  5.769669532775879
8	  5.157710552215576
12	 5.265762567520142
16	 5.468385457992554
32	 6.696927785873413

i7-10700K/Windows11/WSL2(8 core 16 thread)

$ uname -a
Linux DESKTOP-P8UNEDG 5.10.60.1-microsoft-standard-WSL2 #1 SMP Wed Aug 25 23:20:18 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
------------------------------
Pool 経過時間
1	  1.669569969177246
2	  0.8932185173034668
4	  0.5116889476776123
8	  0.4094092845916748
12	 0.35788488388061523
16	 0.4156327247619629
32	 0.38041210174560547

----VirtualBOX
1: 2.208763360977173
2: 3.9075162410736084
4: 3.83050274848938

---WSL2 windows11 再インストール後
1: 1.7535254955291748
2: 0.9307124614715576
4: 0.5294575691223145

i7-8750H Windows10/WSL2( (6 core 12 thread)

Linux DESKTOP-NI63ODB 5.10.60.1-microsoWindows11/WSL2(ft-standard-WSL2 #1 SMP Wed Aug 25 23:20:18 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
-----------------------------------
1: 4.477121353149414
2: 2.522892475128174
4: 1.4394028186798096
8: 1.1422605514526367

Core(TM) i7-3770/WSL2(4 core 8 thread)

Linux mars-PC 4.4.0-19041-Microsoft #1237-Microsoft Sat Sep 11 14:32:00 PST 2021 x86_64 x86_64 x86_64 GNU/Linux
----------------------------------
1: 10.84971809387207
2: 6.165481805801392
4: 4.313432216644287
8: 4.449507713317871

solverと組み合わせて数独を解く

solverを外部のClassファイル化

from __future__ import annotations
from pprint import pformat
from typing import List, Set, Tuple

ROWS = COLS = 9
NUMBERS = [x for x in range(1, 9 + 1)]

class Grid:
    """数独のクイズを表すグリッド"""
    _values: List[List[int]]
    def __init__(self, values: List[List[int]]):
        assert isinstance(values, list)
        assert len(values) == ROWS
        for row in values:
            assert isinstance(row, list)
            assert len(row) == COLS

        self._values = values

    def __hash__(self):
        """hashable 化するための __hash__ 定義
        - set() で利用するため
        """
        return hash(''.join(str(x) for row in self._values for x in row))

    def __str__(self):
        """`print()` で出力されたときの表現を定義する"""
        return '{}(\n{}\n)'.format(type(self).__name__, pformat(self._values))

    def solved(self) -> bool:
        """空セルがなくなったかどうかを判定する"""
        all_values = [x for row in self._values for x in row]
        return 0 not in all_values

    def possible_numbers(self) -> List[Tuple[int, int, List[int]]]:
        """すべての空セルと入りうる数字の組み合わせを全件洗い出す"""
        return [
            (row, col, self._possible_numbers_for_cell(row, col))
            for row, values in enumerate(self._values)
            for col, x in enumerate(values)
            if x == 0
        ]

    def clone_filled(self, row, col, number) -> Grid:
        """特定のセルに指定された値が入った新しい grid を返す"""
        values = [[x for x in row] for row in self._values]
        values[row][col] = number
        return type(self)(values)

    def _possible_numbers_for_cell(self, row, col) -> List[int]:
        row_numbers = [x for x in self._values[row]]
        col_numbers = [row[col] for row in self._values]
        block_numbers = self._block_numbers(row, col)

        return [
            x
            for x in NUMBERS
            if (x not in row_numbers)
            and (x not in col_numbers)
            and (x not in block_numbers)
        ]

    def _block_numbers(self, row, col) -> List[int]:
        row_start = (row // 3) * 3
        col_start = (col // 3) * 3
        return [
            x
            for row in self._values[row_start : row_start + 3]
            for x in row[col_start : col_start + 3]
        ]

def solve_all(grid: Grid) -> Set[Grid]:
    """指定された数独に対する解を全件返す"""
    solutions = set()

    def _solve(grid: Grid):
        # S4. 空のセルがなくなったら正解として追加
        if grid.solved():
            solutions.add(grid)
            return

        # S1. すべてのセルに対して入りうる数字をリストアップする
        possible_numbers = grid.possible_numbers()

        # S2 + S3. 入りうち数字が最も少ないセルに仮に数字を入れて再帰
        row, col, numbers = min(possible_numbers, key=lambda x: len(x[-1]))

        # S5. 入りうる数字がひとつも無い空のセルがある場合はそのルートは間違いなので終了
        if not numbers:
            return

        for number in numbers:
            next_grid = grid.clone_filled(row, col, number)
            _solve(next_grid)

    _solve(grid)

    return solutions

OCRからsolverを呼び出して、数独の回答を表示する。

import streamlit as st
import cv2
from PIL import Image           # 画像処理ライブラリ
import numpy as np              # データ分析用ライブラリ
#import os                       # os の情報を扱うライブラリ
import pytesseract              # tesseract の python 用ライブラリ
import unicodedata
import pprint
from typing import List, Set, Tuple
import solver

ROWS = COLS = 9
NUMBERS = [x for x in range(1, 9 + 1)]

def disp(ans):
        m1="<span style=\"color: red; \">"
        m2="</span>"
        msg="### "
        for x in range(9):
            for y in range(9):
                c=ans[x][y]
                c=str(c)
                if c != "0":
                    msg=msg+m1+c+m2
                else:
                    msg=msg+c
            msg=msg+'<br>'

        msg=msg+'<br>'
        st.markdown(msg,unsafe_allow_html=True)

def remove_control_characters(s):
    return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")

def erase_lines(img,img_thresh,th1):
    # OpenCVで直線の検出
    # https://qiita.com/tifa2chan/items/d2b6c476d9f527785414
    img2 = img.copy()
    img3 = img.copy()
    gray = cv2.cvtColor(img_thresh, cv2.COLOR_BGR2GRAY)
    gray_list = np.array(gray)
    gray2 = cv2.bitwise_not(gray)
    gray2_list = np.array(gray2)
    #lines = cv2.HoughLinesP(gray2, rho=1, theta=np.pi/360, threshold=th1, minLineLength=80, maxLineGap=5)
    lines = cv2.HoughLinesP(gray2, rho=1, theta=np.pi/360, threshold=th1, minLineLength=150, maxLineGap=5)
    xmin,ymin=500,500
    xmax,ymax=0,0
    if lines is not None:

        for line in lines:
            x1, y1, x2, y2 = line[0]
            if x1<xmin:
                xmin=x1
            if y1<ymin:
                ymin=y1
            if x1>xmax:
                xmax=x1
            if y1>ymax:
                ymax=y1

            # 緑色の線を引く
            red_lines_img = cv2.line(img2, (x1,y1), (x2,y2), (0,255,0), 3)
            red_lines_np=np.array( red_lines_img)
            #cv2.imwrite("calendar_mod3.png", red_lines_img)

            # 線を消す(白で線を引く)
            no_lines_img = cv2.line(img_thresh, (x1,y1), (x2,y2), (255,255,255), 3)
            no_lines=np.array( no_lines_img)
        dx=int(0.5+(xmax-xmin)/9)
        dy=int(0.5+(ymax-ymin)/9)
        sx=int(0.5+dx*0.05)
        sy=int(0.5+dy*0.05)
        st.write(xmin,ymin,xmax,ymax,dx,dy)
        peaces=[]
        for y in range(9):
            for x in range(9):
                p = xmin + x*dx + sx
                q = ymin + y*dy + sy
                cv2.rectangle(no_lines,(p,q),(p+dx-sx,q+dy-sy),(0,0,255),1)
                peaces.append(cv2.cvtColor(no_lines_img[q:q+dy-sy,p:p+dx-sx],cv2.COLOR_BGR2RGB))
                #st.image(peace,caption=str(x)+','+str(y))
        im_h= cv2.hconcat([red_lines_img, no_lines])
    else:
        im_h = None
        no_lines = img_thresh
    return im_h, no_lines,peaces

def main():
    st.title('文字認識の実験')
    col1, col2 ,col3, col4 = st.columns([3,1,1,1])
    KEI = None
    with col1:
        uploaded_file = st.file_uploader("画像ファイルを選択してアップロード")
    if uploaded_file is not None:
        img = Image.open(uploaded_file)
        img = np.array(img)
        th2 = st.slider(label='2値化の閾値',min_value=0, max_value=255, value=100)
        th1 = st.slider(label='線消去の閾値',min_value=0, max_value=255, value=100)
        with col2:
            LNG =  st.selectbox("言語選択",['eng','jpn'])
        with col3:
            KEI = st.checkbox('線削除')
        with col4:
            OCR = st.checkbox('OCR実行')

        ret, img_thresh = cv2.threshold(img, th2, 255, cv2.THRESH_BINARY)
        im_h = cv2.hconcat([img, img_thresh])
        st.image(im_h, caption='元画像<--->2値化画像')
        if KEI:
            im_h, no_lines, peaces = erase_lines(img,img_thresh,th1)
            if im_h is None:
                st.warning('No line detectd')
            else:
                new_image = cv2.cvtColor(im_h, cv2.COLOR_BGR2RGB)
                st.image(new_image,caption='線を削除した画像')
        else:
            no_lines=img_thresh

        if OCR:
            my_bar = st.progress(0)
            st.subheader('[OCR結果]')
            #txt = pytesseract.image_to_string(no_lines, lang="eng",config='--psm 11')
            conf='-l ' + LNG + ' --psm 6  outputbase digits'
            n=0
            row=[]
            for peace in peaces:
                txt=pytesseract.image_to_string(peace, config=conf)
                txt=remove_control_characters(txt)
                if txt.isdigit():
                    ans=int(txt)
                else:
                    ans=0
                row.append(ans)
                my_bar.progress(int(100*n/80))
                n=n+1

            row2=np.array(row).reshape(-1,9).tolist()
            st.success(row2)
            #st.write(row2)

            grid = solver.Grid(row2)
            results = solver.solve_all(grid)
            st.subheader('[数独回答]')
            m1="<span style=\"color: darkgray; \">"
            m2="</span>"
            msg="### "
            for r in results:
                buf=[]
                for y in range(9):
                    for x in range(9):
                        buf.append(r._values[y][x])
                        c = r._values[y][x]
                        c = str(c)
                        d = row2[y][x]
                        if d != 0:
                            msg=msg + m1 + c + m2
                        else:
                            msg=msg + c
                    msg=msg + '<br>'
                msg=msg + "<br>"
                st.markdown(msg,unsafe_allow_html=True)
#
#                b = np.array(buf)
#                disp(b)
#                c=b.reshape(-1,9)
#                st.write(c)
if __name__ == '__main__':
    main()

行例:

数独の問題画像から数字を自動で切り出す

認識に邪魔な線をcv2.HoughLinesPの機能で判定しているわけですが、この関数では、線として認識した座標(x0,y0)-(x1,y1)のリストを返します。

この座標群から, Xmin,Xmax,Ymin,Ymaxを求めると、問題マスの左上と右下の座標が分かります。

線を認識する際に、少なくても左端と右端の垂直線、水平線の上端、下端の線を認識できていることが条件です。

次の画像では、緑が線として認識した箇所。4個の青で囲んだ線が、位置の判定に必要な線です。4本の線の座標から、赤丸の座標も算出できます。

これらの座標から、1文字毎の画像を切り出して個々の画像を認識するよう実装してみました。

import streamlit as st
import cv2
from PIL import Image           # 画像処理ライブラリ
#from matplotlib import pyplot as plt # データプロット用ライブラリ
import numpy as np              # データ分析用ライブラリ
#import os                       # os の情報を扱うライブラリ
import pytesseract              # tesseract の python 用ライブラリ
import unicodedata

def remove_control_characters(s):
    return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")

def erase_lines(img,img_thresh,th1):
    # OpenCVで直線の検出
    # https://qiita.com/tifa2chan/items/d2b6c476d9f527785414
    img2 = img.copy()
    img3 = img.copy()
    gray = cv2.cvtColor(img_thresh, cv2.COLOR_BGR2GRAY)
    gray_list = np.array(gray)
    gray2 = cv2.bitwise_not(gray)
    gray2_list = np.array(gray2)
    lines = cv2.HoughLinesP(gray2, rho=1, theta=np.pi/360, threshold=th1, minLineLength=150, maxLineGap=5)
    xmin,ymin=500,500
    xmax,ymax=0,0
    if lines is not None:
        for line in lines: # Xmin,Xmax,Ymin,Ymaxの算出
            x1, y1, x2, y2 = line[0]
            if x1<xmin:
                xmin=x1
            if y1<ymin:
                ymin=y1
            if x1>xmax:
                xmax=x1
            if y1>ymax:
                ymax=y1

            # 緑色の線を引く
            red_lines_img = cv2.line(img2, (x1,y1), (x2,y2), (0,255,0), 3)
            red_lines_np=np.array( red_lines_img)
            #cv2.imwrite("calendar_mod3.png", red_lines_img)

            # 線を消す(白で線を引く)
            no_lines_img = cv2.line(img_thresh, (x1,y1), (x2,y2), (255,255,255), 3)
            no_lines=np.array( no_lines_img)
        dx=int(0.5+(xmax-xmin)/9)
        dy=int(0.5+(ymax-ymin)/9)
        sx=int(0.5+dx*0.2)
        sy=int(0.5+dy*0.2)
        st.write(xmin,ymin,xmax,ymax,dx,dy)
        peaces=[]
        for y in range(9):
            for x in range(9):
                p = xmin + x*dx + sx
                q = ymin + y*dy + sy
                cv2.rectangle(no_lines,(p,q),(p+dx-sx,q+dy-sy),(0,0,255),1)
                peaces.append(cv2.cvtColor(no_lines_img[q:q+dy-sy,p:p+dx-sx],cv2.COLOR_BGR2RGB))
                #st.image(peace,caption=str(x)+','+str(y))
        im_h= cv2.hconcat([red_lines_img, img_thresh])
    else:
        im_h = None
        no_lines = img_thresh
    return im_h, no_lines,peaces

def main():
    st.title('文字認識の実験')
    col1, col2 ,col3, col4 = st.columns([3,1,1,1])
    KEI = None
    with col1:
        uploaded_file = st.file_uploader("画像ファイルを選択してアップロード")
    if uploaded_file is not None:
        img = Image.open(uploaded_file)
        img = np.array(img)
        th2 = st.slider(label='2値化の閾値',min_value=0, max_value=255, value=100)
        th1 = st.slider(label='線消去の閾値',min_value=0, max_value=255, value=100)
        with col2:
            LNG =  st.selectbox("言語選択",['eng','jpn'])
        with col3:
            KEI = st.checkbox('線削除')
        with col4:
            OCR = st.checkbox('OCR実行')

        ret, img_thresh = cv2.threshold(img, th2, 255, cv2.THRESH_BINARY)
        im_h = cv2.hconcat([img, img_thresh])
        st.image(im_h, caption='元画像<--->2値化画像')
        if KEI:
            im_h, no_lines, peaces = erase_lines(img,img_thresh,th1)
            if im_h is None:
                st.warning('No line detectd')
            else:
                new_image = cv2.cvtColor(im_h, cv2.COLOR_BGR2RGB)
                st.image(new_image,caption='線を削除した画像')
        else:
            no_lines=img_thresh

        if OCR:
            st.subheader('---認識結果---')
            #txt = pytesseract.image_to_string(no_lines, lang="eng",config='--psm 11')
            conf='-l ' + LNG + ' --psm 6  outputbase digits'
            n=0
            row=''
            for peace in peaces:
                txt=pytesseract.image_to_string(peace, config=conf)
                txt=remove_control_characters(txt)
                if txt.isdigit():
                    ans=str(txt)+' '
                else:
                    ans='--'
                row=row+ans
                n=n+1
                if(n==9):
                    st.write(row)
                    row=''
                    n=0
if __name__ == '__main__':
    main()

認識結果:100%の正解

1 --------6 8 ----
5 ------------3 --
------7 2 --------
9 4 2 ----3 7 --6
7 --3 ----4 1 ----
8 --5 ----9 ------
4 7 6 3 9 2 5 8 1
2 5 1 --8 7 3 6 --
--9 8 1 6 5 4 2 --

文字認識の条件設定をインタラクティブに

streamlitを利用して、ファイルの選択、言語設定、枠線削除のON/OFF、2値化と線消去の閾値をインタラクティブに設定できるようにしてみました。

表示する画像は、元画像と2値化した画像。線削除を選択すると、削除する線と元画像から線を削除した画像を表示します。

OCR実行をチェックすると、pytesseractで文字の認識を行います。文字認識の処理には時間がかかるので、閾値の設定中はチェックしないほうが良い。

処理結果の一例

pythonのコード

import streamlit as st
import cv2
from PIL import Image           # 画像処理ライブラリ
from matplotlib import pyplot as plt # データプロット用ライブラリ
import numpy as np              # データ分析用ライブラリ
import os                       # os の情報を扱うライブラリ
import pytesseract              # tesseract の python 用ライブラリ
import glob

files=glob.glob("/home/mars/streamlit/*png")
#print(files)
def main():
    st.title('文字認識の実験')
    col1, col2 ,col3, col4 = st.columns([3,1,1,1])

    with col1:
        TGT = st.selectbox("ファイルの選択",files)
    th2 = st.slider(label='2値化の閾値',min_value=0, max_value=255, value=100)
    th1 = st.slider(label='線消去の閾値',min_value=0, max_value=255, value=100)
    with col2:
        LNG =  st.selectbox("言語選択",['jpn','eng','number'])
    with col3:
        KEI = st.checkbox('線削除')
    with col4:
        OCR = st.checkbox('OCR実行')

    img = cv2.imread(TGT)
    #with pict[0]:
    st.write('画像',TGT,img.shape)
    #img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
    ret, img_thresh = cv2.threshold(img, th2, 255, cv2.THRESH_BINARY)
    im_h = cv2.hconcat([img, img_thresh])
    st.image(im_h, caption='元画像')
    if KEI:
        img2 = img.copy()
        img3 = img.copy()
        gray = cv2.cvtColor(img_thresh, cv2.COLOR_BGR2GRAY)
        gray_list = np.array(gray)
        #img2.image(gray_list, caption='GRAY',use_column_width=True)
        gray2 = cv2.bitwise_not(gray)
        gray2_list = np.array(gray2)
        lines = cv2.HoughLinesP(gray2, rho=1, theta=np.pi/360, threshold=th1, minLineLength=80, maxLineGap=5)

        for line in lines:
            x1, y1, x2, y2 = line[0]

            # 緑色の線を引く
            red_lines_img = cv2.line(img2, (x1,y1), (x2,y2), (0,255,0), 3)
            red_lines_np=np.array( red_lines_img)
            #cv2.imwrite("calendar_mod3.png", red_lines_img)

            # 線を消す(白で線を引く)
            no_lines_img = cv2.line(img_thresh, (x1,y1), (x2,y2), (255,255,255), 3)
            no_lines=np.array( no_lines_img)
        im_h = cv2.hconcat([red_lines_img, no_lines_img])
        st.image(im_h,caption='No lines')
    else:
        no_lines=img_thresh

    if OCR:
        #txt = pytesseract.image_to_string(no_lines, lang="eng",config='--psm 11')
        conf='-l ' + LNG + ' --psm6'
        txt=pytesseract.image_to_string(no_lines, config=conf)

        st.write(txt)

if __name__ == '__main__':
    main()