Powetool キーボードの入れ替え
ElevenClock 時計関連(秒の表示など)
defenderのログ(eventlog)
Powetool キーボードの入れ替え
ElevenClock 時計関連(秒の表示など)
defenderのログ(eventlog)
必要なツールをインストール
$ sudo apt install mpack
$ sudo apt install libemail-outlook-message-perl libemail-sender-perl
/dev/sdb1 がFAT32のUSBで、/mnt/tmpへマウント
mount -t vfat -o defaults,utf8 /dev/sdb1 /mnt/tmp
msgファイルをemlへ変換
$ msgconvert *.msg
*emlファイルが生成されるので、emlファイルを解析
$ munpack *eml
raspberryPiで利用できるマルチブートの環境として、PINN(PINN IsNotNoobs)とBerrybootなどが知られている。
PINNでカスタムOSを利用する手順のリンク。
結構煩雑な手順を踏む必要がある。
PINN用のカスタムOS設定に必要なファイル
https://sourceforge.net/projects/pinn/files/
BerryBoot用に設定されたOSイメージのダウンロードリンク
https://sourceforge.net/projects/berryboot/files/
PINNよりも公開されているカスタムOSの種類が多く、さらに導入手順も比較的容易。実際に試してみたところ、swapをONにする設定ができない模様。
その後の調査でBerryBootのファイルシステムはoverlayとなっていてることがswapを設定できない原因らしい。最近のRaspberry OSでは、swapの設定を/sbin/dphys-swapfileのスクリプトで実行しているが、こんな記事を発見。
[Solved] With overlay of root, swap does not work
解決方法として:
〇dphys-swapfileを利用しない。
〇swapとしてファイルではなく、(overlayとは別の?)デバイスを指定する必要があると理解。
次のように、SSDのパーティションにswap用に4GBを割り当ててみました。
evice Boot Start End Sectors Size Id Type
/dev/sda1 2048 262143 260096 127M e W95 FAT16 (LBA)
/dev/sda2 47955968 468860927 420904960 200.7G 83 Linux
/dev/sda3 39567360 47955967 8388608 4G 82 Linux swap / Solaris
swapファイルの指定は、昔ながらの(?)/etc/fstabに記述します。
/dev/sda3 swap swap defaults 0 0
その結果、4GBのswap領域を確保することができた。
$ free
total used free shared buff/cache available
Mem: 3798908 307284 579676 47612 2911948 3366744
Swap: 4194300 0 4194300
BerryBootでインストールした複数のOS毎に、以上のようにswap設定を変更することで、目的を達成!
最初にASI_linux_mac_SDK_V1.21をインストール
ダウンロードのリンク
Software and Drivers
ダウンロードして展開すると;
$ls ASI_linux_mac_SDK_V1.21
demo doc include lib license.txt
$ls ASI_linux_mac_SDK_V1.21/demo
Makefile bin main_SDK2_snap.cpp main_SDK2_video.cpp main_SDK2_video_mac main_SDK2_video_mac.cpp readme.txt
$cat readme.txt
Please install opencv2 at first, for example under Linux x86 OS, run:
make platform=x86
Below options is supported:
x86(Linux 32-bit)
x64(Linux 64-bit)
armv5
armv6
armv7
armv8
mac32
mac64
mac(32-bit and 64-bit)
If libASICamera2.so or (.dylib) can't be found at run time, resolve by delow two ways:
1.Add a .conf file that contains the path of the library to /etc/ld.so.conf.d/, run ldconfig.
2.Add compile option -Wl,-rpath=<library path>
raspberry pi4 64bit OSの場合 armv8
makeに先立って、demo/binの下へarmv8フォルダーを作る。
/usr/includeの下に、opencv2のファイル一式が必要。
root権限なしでカメラをアクセスできるようにするためのルールを追加
設定ファイル ASI_linux_mac_SDK_V1.21lib/asi.rulesをlib/udev/rules.d または/etc/udev/rules.dへCopyして、カメラを抜き差し。
$cd demo
$mkdir demo/bin/armv8
$make platform=armv8
$ ls demo/bin/armv8/
libASICamera2.so main_SDK2_video_mac test_gui2_snap test_gui2_video
libASICamera2.so を参照できるよう、適切なフォルダーへ配置しldconfig
(あまり良い方法ではないが、/lib の下へlibASICamera2.soをCopy)
Pythonから利用できるようにライブラーをインストール
https://github.com/python-zwoasi/python-zwoasi
#!/usr/bin/env python
import argparse
import os
import sys
import time
import zwoasi as asi
env_filename = os.getenv('ZWO_ASI_LIB')
ZWO_ASI_LIBには、次のパスを設定
$ echo $ZWO_ASI_LIB
/home/pi/python-zwoasi/build/lib/zwoasi
$ ls /home/pi/python-zwoasi/build/lib/zwoasi
__init__.py の内容
"""Interface to ZWO ASI range of USB cameras.
Calls to the `zwoasi` module may raise :class:`TypeError` or :class:`ValueError` exceptions if an input argument
is incorrect. Failure conditions from within the module may raise exceptions of type :class:`ZWO_Error`. Errors from
conditions specifically from the SDK C library are indicated by errors of type :class:`ZWO_IOError`; certain
:func:`Camera.capture()` errors are signalled by :class:`ZWO_CaptureError`."""
import ctypes as c
from ctypes.util import find_library
import logging
import numpy as np
import os
import six
import sys
import time
import traceback
__author__ = 'Steve Marple'
__version__ = '0.1.0.1'
__license__ = 'MIT'
def get_num_cameras():
"""Retrieves the number of ZWO ASI cameras that are connected. Type :class:`int`."""
return zwolib.ASIGetNumOfConnectedCameras()
$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.')
右上がりの破線状の軌跡は恒星の日周運動。
参考にしたサイト
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を外部の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 --