python Tesseract-OCR문자 인식 exe실행 프로그램에서 오류 발생 OSError: [WinError 6] The handle is invalid

공유하기

  • Add this entry to Hatena Bookmark
  • 0

Tesseract-OCR 문자 인식 패키지를 사용해 이미지의 문자를 텍스트 문자로 변환 해주는 프로그램 test.py파일을 작성했습니다. 그리고, python test.py로 문제 없이 잘 실행 되는것도 확인했었습니다. 그런데 pyinstaller --onefile --icon=test.ico --clean --windowed test.py 로 Windows 실행 파일 test.exe를 만들어 실행했더니 다음과 같은 tesseract.image_to_string를 부르는 곳에서 에러가 발생했습니다. 이 글에서는 다음과 같은 오류에 접했을 때 해결 하는 방법을 소개합니다.

Traceback (most recent call last):
  File "test.py", line 204, in testocr
  File "pyocr\tesseract.py", line 364, in image_to_string
  File "pyocr\tesseract.py", line 293, in run_tesseract
  File "subprocess.py", line 829, in __init__
  File "subprocess.py", line 1252, in _get_handles
OSError: [WinError 6] The handle is invalid

전제 조건

이 글은 다음 내용들이 적용 되어있는 환경에서 실행되었습니다.

  1. python 설치 프로그램 다운로드
  2. 최신 버전 python3.9 64bit 설치
  3. python 설치 결과 확인을 위해 실행해 보기
  4. pip 버전 업그레이드
  5. Python 추가 패키지 ‘pyautogui‘ 설치
  6. Python추가 패키지 ‘PIL‘ 설치
  7. Python 추가 패키지 numpy‘ 설치
  8. Python 추가 패키지 ‘cv2‘ 설치
  9. Python의 py파일을 exe실행파일로 변화해주는Python 추가 패키지 pyinstaller‘ 설치

pyocr\tesseract.py 문제의 코드

File "pyocr\tesseract.py", line 293, in run_tesseract 의 에러 추적 정보를 가지고 다음과 같이 소스를 확인해 보았더니 stdin에 대해서는 인수를 설정하지 않고 있습니다.

 293:   proc = subprocess.Popen(command, cwd=cwd,
 294:                            startupinfo=g_subprocess_startup_info,
 295:                            creationflags=g_creation_flags,
 296:                            stdout=subprocess.PIPE,
 297:                            stderr=subprocess.STDOUT)

에러 장소 소스 확인

subprocess.Popen를 실행할 때 stdin에 대해서는 인수를 설정하지 않은 결과 에러 메시지 File "subprocess.py", line 829, in __init__ 함수의 756:행에서 인수 stdin=None 의 기본 값은 바뀌지 않았으며 그리고 1251:행에서 if stdin is None 조건에 일치하여 에러 메시지 File "subprocess.py", line 1252, in _get_handleswinapi.GetStdHandle(_winapi.STD_INPUT_HANDLE)에서 에러가 발생했음을 알 수 있습니다.

console용으로 실행할 때는 _winapi.STD_INPUT_HANDLE 을 취득할 수 있지만 --noconsole 옵션으로 컴파일한 Windows용 exe실행 파일은 _winapi.STD_INPUT_HANDLE을 취득하지 못하는 것 같습니다.

만약 1260:행의 elif stdin == DEVNULL 로 조건 분기하도록 수정할 수 있다면 오류를 해결할 수 있을 것 같습니다.

 756: def __init__(self, args, bufsize=-1, executable=None,
 756:           stdin=None, stdout=None, stderr=None,
 756:           preexec_fn=None, close_fds=True,
 756:           shell=False, cwd=None, env=None, universal_newlines=None,
 756:           startupinfo=None, creationflags=0,
 756:           restore_signals=True, start_new_session=False,
 756:           pass_fds=(), *, user=None, group=None, extra_groups=None,
 756:           encoding=None, errors=None, text=None, umask=-1):
.... 중간생략 
 827:     (p2cread, p2cwrite,
 828:      c2pread, c2pwrite,
 829:      errread, errwrite) = self._get_handles(stdin, stdout, stderr)
.... 중간생략
1251: def _get_handles(self, stdin, stdout, stderr):
.... 중간생략
1251:     if stdin is None:
1252:         p2cread = _winapi.GetStdHandle(_winapi.STD_INPUT_HANDLE)

.... 중간생략
1257:     elif stdin == PIPE:
1258:         p2cread, p2cwrite = _winapi.CreatePipe(None, 0)
1259:         p2cread, p2cwrite = Handle(p2cread), Handle(p2cwrite)
1260:     elif stdin == DEVNULL:
1261:         p2cread = msvcrt.get_osfhandle(self._get_devnull())
1262:     elif isinstance(stdin, int):
1263:         p2cread = msvcrt.get_osfhandle(stdin)
1264:     else:
1265:         # Assuming file-like object
1266:         p2cread = msvcrt.get_osfhandle(stdin.fileno())

pyocr\tesseract.py 소스 수정

다음과 같이 인수를 추가하여 stdin=subprocess.DEVNULL를 건네주도록 수정합니다. subprocess.py 소스에서 __init__함수의 stdin의 기본 설정 None이 subprocess.DEVNULL로 변경되어 1260:행 elif stdin == DEVNULL:로 조건 분기됩니다.

 293:   proc = subprocess.Popen(command, cwd=cwd,
 294:                            startupinfo=g_subprocess_startup_info,
 295:                            creationflags=g_creation_flags,
 296:                            stdout=subprocess.PIPE,
 297:                            stderr=subprocess.STDOUT,
 298:                            stdin=subprocess.DEVNULL)

재컴파일 후 Windows 실행 파일 test.exe를 다시 실행 하면 오류가 발생하지 않는 것을 확인할 수 있습니다.

위의 내용으로 추측하건대 Tesseract-OCR의 작성자들은 pyinstaller를 이용한 --noconsole 옵션으로 Windows GUI exe파일을 작성해서 winapi.GetStdHandle(_winapi.STD_INPUT_HANDLE)에서 문제가 발생했던 경우가 지금까지 보고 되지 않아서 수정을 못하지 않았나 싶습니다.