테서 랙트 OCR 정확도 향상을위한 이미지 처리


145

tesseract를 사용하여 문서를 텍스트로 변환했습니다. 문서의 품질은 매우 다양하며 어떤 종류의 이미지 처리가 결과를 향상시킬 수 있는지에 대한 팁을 찾고 있습니다. 팩스 장치에서 생성 된 것과 같이 픽셀 화가 잘 된 텍스트는 특히 테서 랙트가 처리하기 어려운 것으로 나타났습니다.

어떤 종류의 이미지 처리 기술로 정확도가 향상됩니까? 가우시안 블러를 사용하여 픽셀 화 된 이미지를 부드럽게하고 약간의 개선을 보았지만 더 나은 결과를 얻을 수있는 더 구체적인 기술이 있기를 바랍니다. 흑백 이미지로 조정 된 필터를 사용하여 불규칙한 가장자리를 매끄럽게 한 다음 대비를 높여서 문자를 더 뚜렷하게 만듭니다.

이미지 처리에 초보자 인 사람을위한 일반적인 팁이 있습니까?

답변:


103
  1. DPI 수정 (필요한 경우) 300 DPI 이상
  2. 텍스트 크기 수정 (예 : 12pt는 괜찮아 야 함)
  3. 텍스트 줄을 수정하려고합니다 (텍스트를 줄이거 나 줄임)
  4. 이미지의 조명을 수정하려고합니다 (예 : 이미지의 어두운 부분 없음)
  5. 이진화 및 노이즈 제거 이미지

모든 경우에 맞는 범용 명령 줄이 없습니다 (때로는 이미지를 흐리게하고 선명하게해야 함). 그러나 Fred의 ImageMagick Scripts에서 TEXTCLEANER를 사용해 볼 수 있습니다 .

커맨드 라인을 좋아하지 않는다면 opensource scantailor.sourceforge.net 또는 상업용 bookrestorer를 사용해보십시오 .


6
이를 수행하는 방법에 대한 가이드가 있습니다 : code.google.com/p/tesseract-ocr/wiki/ImproveQuality
iljau

2
링크 된 스크립트는 리눅스 전용 인 것으로 보입니다.
Zoran Pavlovic

1
이것은 사실이 아닙니다-이것은 bash 스크립트입니다. bash 및 ImageMagick을 설치 한 경우 Windows에서도 실행됩니다. Bash는 git 또는 msys2와 같은 다른 유용한 소프트웨어의 일부로 설치 될 수 있습니다 .
user898678

6
@iljau 이후 github으로 옮겨졌습니다. wiki 페이지는 다음 위치에 있습니다 : github.com/tesseract-ocr/tesseract/wiki/ImproveQuality
hometoast


73

나는 결코 OCR 전문가가 아닙니다. 그러나 이번 주에는 jpg에서 텍스트를 변환해야했습니다.

색상이 지정된 RGB 445x747 픽셀 jpg로 시작했습니다. 나는 즉시 이것에 대해 tesseract를 시도했으며 프로그램은 거의 아무것도 변환하지 않았습니다. 그런 다음 김프에 들어가서 다음을 수행했습니다. 이미지> 모드> 회색조 이미지> 스케일 이미지> 1191x2000 픽셀 필터> 향상> 반경 값 = 6.8, 양 = 2.69, 임계 값 = 0 인 언샵 마스크 100 % 품질로 새 jpg로 저장했습니다.

그런 다음 Tesseract는 모든 텍스트를 .txt 파일로 추출 할 수있었습니다.

김프는 당신의 친구입니다.


11
+1 나는 당신의 발자취를 따라 갔으며 크게 개선되었습니다. 감사합니다
onof

1
또한 입력을 TIFF 파일로 변환하고 Tesseract에 TIFF를 제공하면 Tesseract가 더 잘 작동한다는 인상을 받았습니다. ImageMagick이 변환을 수행 할 수 있습니다. 이것은 일화적인 인상이지만 신중하게 테스트하지 않았으므로 잘못되었을 수 있습니다.
DW

+1 "언샵 마스크"필터가 제 하루를 실제로 만들었습니다. 도움이되는 또 다른 단계 : "퍼지 선택"도구를 사용하여 배경을 선택한 다음 Del을 눌러 축소하십시오
Davide

tesseract 인식 전에이 이미지 처리 문제에 갇혀 있습니다. stackoverflow.com/questions/32473095/… 여기서 도와 드릴까요?
Hussain

아니. 나는 그것을 더 큰 크기로 만들고 그레이 스케일로 설정하려고 시도하면 긍정적 인 결과를 얻지 못하는 것 같습니다. 한숨 :(이 목표를 확인 : freesms4us.com/…
gumuruh

30

이미지의 가독성을 높이기위한 세 가지 점 : 1) 가변 높이와 너비로 이미지 크기를 조정합니다 (이미지 높이와 너비에 0.5와 1, 2를 곱함). 2) 이미지를 그레이 스케일 형식 (흑백)으로 변환합니다. 3) 노이즈 픽셀을 제거하고 더 선명하게 만듭니다 (이미지 필터링).

아래 코드를 참조하십시오 :

//Resize
  public Bitmap Resize(Bitmap bmp, int newWidth, int newHeight)
        {

                Bitmap temp = (Bitmap)bmp;

                Bitmap bmap = new Bitmap(newWidth, newHeight, temp.PixelFormat);

                double nWidthFactor = (double)temp.Width / (double)newWidth;
                double nHeightFactor = (double)temp.Height / (double)newHeight;

                double fx, fy, nx, ny;
                int cx, cy, fr_x, fr_y;
                Color color1 = new Color();
                Color color2 = new Color();
                Color color3 = new Color();
                Color color4 = new Color();
                byte nRed, nGreen, nBlue;

                byte bp1, bp2;

                for (int x = 0; x < bmap.Width; ++x)
                {
                    for (int y = 0; y < bmap.Height; ++y)
                    {

                        fr_x = (int)Math.Floor(x * nWidthFactor);
                        fr_y = (int)Math.Floor(y * nHeightFactor);
                        cx = fr_x + 1;
                        if (cx >= temp.Width) cx = fr_x;
                        cy = fr_y + 1;
                        if (cy >= temp.Height) cy = fr_y;
                        fx = x * nWidthFactor - fr_x;
                        fy = y * nHeightFactor - fr_y;
                        nx = 1.0 - fx;
                        ny = 1.0 - fy;

                        color1 = temp.GetPixel(fr_x, fr_y);
                        color2 = temp.GetPixel(cx, fr_y);
                        color3 = temp.GetPixel(fr_x, cy);
                        color4 = temp.GetPixel(cx, cy);

                        // Blue
                        bp1 = (byte)(nx * color1.B + fx * color2.B);

                        bp2 = (byte)(nx * color3.B + fx * color4.B);

                        nBlue = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Green
                        bp1 = (byte)(nx * color1.G + fx * color2.G);

                        bp2 = (byte)(nx * color3.G + fx * color4.G);

                        nGreen = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        // Red
                        bp1 = (byte)(nx * color1.R + fx * color2.R);

                        bp2 = (byte)(nx * color3.R + fx * color4.R);

                        nRed = (byte)(ny * (double)(bp1) + fy * (double)(bp2));

                        bmap.SetPixel(x, y, System.Drawing.Color.FromArgb
                (255, nRed, nGreen, nBlue));
                    }
                }



                bmap = SetGrayscale(bmap);
                bmap = RemoveNoise(bmap);

                return bmap;

        }


//SetGrayscale
  public Bitmap SetGrayscale(Bitmap img)
        {

            Bitmap temp = (Bitmap)img;
            Bitmap bmap = (Bitmap)temp.Clone();
            Color c;
            for (int i = 0; i < bmap.Width; i++)
            {
                for (int j = 0; j < bmap.Height; j++)
                {
                    c = bmap.GetPixel(i, j);
                    byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);

                    bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
                }
            }
            return (Bitmap)bmap.Clone();

        }
//RemoveNoise
   public Bitmap RemoveNoise(Bitmap bmap)
        {

            for (var x = 0; x < bmap.Width; x++)
            {
                for (var y = 0; y < bmap.Height; y++)
                {
                    var pixel = bmap.GetPixel(x, y);
                    if (pixel.R < 162 && pixel.G < 162 && pixel.B < 162)
                        bmap.SetPixel(x, y, Color.Black);
                    else if (pixel.R > 162 && pixel.G > 162 && pixel.B > 162)
                        bmap.SetPixel(x, y, Color.White);
                }
            }

            return bmap;
        }

이미지 입력
이미지 입력

출력 이미지 출력 이미지


예. 우리는 필요한 매개 변수를 크기 조정 방법으로 전달해야합니다.
Sathyaraj Palanisamy

파일 세트에서이 방법을 시도하여 초기 결과와 비교했습니다. 일부 제한된 경우 더 나은 결과를 얻을 수 있으며 대부분 출력 텍스트 품질이 약간 떨어졌습니다. 따라서 보편적 인 솔루션처럼 보이지 않습니다.
Bryn

이것은 실제로 나를 위해 잘 작동했습니다. 확실히 그것은 이미지 전처리의 출발점을 제공하여 Tesseract에서 돌아온 횡설수설의 양을 제거합니다.
ses

22

일반적으로 OpenCV 라이브러리를 사용하여 다음 이미지 사전 처리 기술을 적용합니다.

  1. 이미지 크기 조정 (DPI가 300dpi 미만인 이미지로 작업하는 경우 권장) :

    img = cv2.resize(img, None, fx=1.2, fy=1.2, interpolation=cv2.INTER_CUBIC)
    
  2. 이미지를 회색조로 변환 :

    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
  3. 노이즈 제거를 위해 팽창 및 침식 적용 (데이터 세트에 따라 커널 크기로 재생 가능) :

    kernel = np.ones((1, 1), np.uint8)
    img = cv2.dilate(img, kernel, iterations=1)
    img = cv2.erode(img, kernel, iterations=1)
    
  4. 다음 선 중 하나를 사용하여 블러를 적용 할 수 있습니다 (각각 장단점이 있지만 중간 블러 및 양방향 필터는 일반적으로 가우시안 블러보다 성능이 우수합니다).

    cv2.threshold(cv2.GaussianBlur(img, (5, 5), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.bilateralFilter(img, 5, 75, 75), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.threshold(cv2.medianBlur(img, 3), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
    
    cv2.adaptiveThreshold(cv2.GaussianBlur(img, (5, 5), 0), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.bilateralFilter(img, 9, 75, 75), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    
    cv2.adaptiveThreshold(cv2.medianBlur(img, 3), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)
    

최근에 Tesseract에 대한 매우 간단한 안내서를 작성했지만 첫 번째 OCR 스크립트를 작성하고 문서에서 원하는 것보다 덜 명확했을 때 경험했던 장애물을 정리할 수 있어야합니다.

당신이 그들을 확인하고 싶다면, 나는 당신과 링크를 공유하고 있습니다 :


왜 이미지를 그레이 스케일로 변환합니까? 좀 더 구체적으로, 이미지 감지 과정에서 이미지를 먼저 그레이 스케일로 변환 한 다음 sobel-> MSER-> SWT로 변환했습니다. 좀 더 자세히 설명해 주시겠습니까? 나는 IP 분야에서 처음이다.
OnePunchMan

내 이해에 관해서는 알고리즘에 따라 다르며 일부는 전혀 변환 할 필요가 없습니다. 픽셀을 RGB, 빨강, 녹색 및 파랑의 경우 디지털 방식으로 저장된 몇 가지 색상 값으로 생각하십시오. 픽셀이 B / W 스케일로 변환되면 알고리즘은 3이 아닌 2 차원에서만 작동해야합니다. 이는 픽셀에서 알고리즘을 하나씩 실행할 때 속도면에서 분명한 이점이 있습니다. 또한 일부는 회색조로 변환 될 때 노이즈를 제거하고 그림의 가장자리를 감지하는 것이 더 쉽다고 말할 수도 있습니다.
bkaankuguoglu

답변 주셔서 감사합니다. 블로그에 대해서는 로마자가 아닌 스크립트에 대해 TESSERACT를 사용하여 스크래치에서 OCR을 작성하는 방법에 대한 블로그를 작성하십시오. 나는 모든 곳을 검색했지만, 가능한 모든 것이 명확하지 않습니다.
OnePunchMan 2016 년

16

이것은 다소 오래되었지만 여전히 유용 할 수 있습니다.

내 경험에 따르면 이미지를 tesseract로 전달하기 전에 메모리 내 이미지 크기를 조정하면 도움이 될 수 있습니다.

다른 보간 모드를 시도하십시오. 게시물 https://stackoverflow.com/a/4756906/146003 이 많은 도움이되었습니다.


15

이런 식으로 나에게 매우 도움이 된 것은 Capture2Text 프로젝트의 소스 코드입니다. http://sourceforge.net/projects/capture2text/files/Capture2Text/ .

BTW : 그런 힘든 알고리즘을 공유해 준 저자에게 좋은 생각입니다.

Capture2Text \ SourceCode \ leptonica_util \ leptonica_util.c 파일에 특별한주의를 기울이십시오. 이것이이 유틸리티의 이미지 전처리의 핵심입니다.

바이너리를 실행하는 경우 Capture2Text \ Output \ 폴더에서 프로세스 전후에 이미지 변환을 확인할 수 있습니다.

PS에서 언급 한 솔루션은 OCR에 Tesseract를 사용하고 전처리에 Leptonica를 사용합니다.


1
Capture2Text 도구에 감사드립니다. 내 프로젝트의 모든 OCR 문제를 완벽하게 해결합니다!
Lê Quang Duy

12

위의 Sathyaraj 코드 용 Java 버전 :

// Resize
public Bitmap resize(Bitmap img, int newWidth, int newHeight) {
    Bitmap bmap = img.copy(img.getConfig(), true);

    double nWidthFactor = (double) img.getWidth() / (double) newWidth;
    double nHeightFactor = (double) img.getHeight() / (double) newHeight;

    double fx, fy, nx, ny;
    int cx, cy, fr_x, fr_y;
    int color1;
    int color2;
    int color3;
    int color4;
    byte nRed, nGreen, nBlue;

    byte bp1, bp2;

    for (int x = 0; x < bmap.getWidth(); ++x) {
        for (int y = 0; y < bmap.getHeight(); ++y) {

            fr_x = (int) Math.floor(x * nWidthFactor);
            fr_y = (int) Math.floor(y * nHeightFactor);
            cx = fr_x + 1;
            if (cx >= img.getWidth())
                cx = fr_x;
            cy = fr_y + 1;
            if (cy >= img.getHeight())
                cy = fr_y;
            fx = x * nWidthFactor - fr_x;
            fy = y * nHeightFactor - fr_y;
            nx = 1.0 - fx;
            ny = 1.0 - fy;

            color1 = img.getPixel(fr_x, fr_y);
            color2 = img.getPixel(cx, fr_y);
            color3 = img.getPixel(fr_x, cy);
            color4 = img.getPixel(cx, cy);

            // Blue
            bp1 = (byte) (nx * Color.blue(color1) + fx * Color.blue(color2));
            bp2 = (byte) (nx * Color.blue(color3) + fx * Color.blue(color4));
            nBlue = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            // Green
            bp1 = (byte) (nx * Color.green(color1) + fx * Color.green(color2));
            bp2 = (byte) (nx * Color.green(color3) + fx * Color.green(color4));
            nGreen = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            // Red
            bp1 = (byte) (nx * Color.red(color1) + fx * Color.red(color2));
            bp2 = (byte) (nx * Color.red(color3) + fx * Color.red(color4));
            nRed = (byte) (ny * (double) (bp1) + fy * (double) (bp2));

            bmap.setPixel(x, y, Color.argb(255, nRed, nGreen, nBlue));
        }
    }

    bmap = setGrayscale(bmap);
    bmap = removeNoise(bmap);

    return bmap;
}

// SetGrayscale
private Bitmap setGrayscale(Bitmap img) {
    Bitmap bmap = img.copy(img.getConfig(), true);
    int c;
    for (int i = 0; i < bmap.getWidth(); i++) {
        for (int j = 0; j < bmap.getHeight(); j++) {
            c = bmap.getPixel(i, j);
            byte gray = (byte) (.299 * Color.red(c) + .587 * Color.green(c)
                    + .114 * Color.blue(c));

            bmap.setPixel(i, j, Color.argb(255, gray, gray, gray));
        }
    }
    return bmap;
}

// RemoveNoise
private Bitmap removeNoise(Bitmap bmap) {
    for (int x = 0; x < bmap.getWidth(); x++) {
        for (int y = 0; y < bmap.getHeight(); y++) {
            int pixel = bmap.getPixel(x, y);
            if (Color.red(pixel) < 162 && Color.green(pixel) < 162 && Color.blue(pixel) < 162) {
                bmap.setPixel(x, y, Color.BLACK);
            }
        }
    }
    for (int x = 0; x < bmap.getWidth(); x++) {
        for (int y = 0; y < bmap.getHeight(); y++) {
            int pixel = bmap.getPixel(x, y);
            if (Color.red(pixel) > 162 && Color.green(pixel) > 162 && Color.blue(pixel) > 162) {
                bmap.setPixel(x, y, Color.WHITE);
            }
        }
    }
    return bmap;
}

비트 맵 수업은 무엇입니까? 비트 맵을 Java에서 찾을 수 없습니다 (기본적으로 Android에 있음).
우리는

이 메소드는 예외를 통해 발생합니다. 원인 : java.lang.IllegalArgumentException : y는 <bitmap.height () 여야합니다.
Nativ

9

Tesseract 문서에는 이미지 처리 단계를 통해 OCR 품질을 향상시키는 방법대한 자세한 내용이 포함되어 있습니다 .

어느 정도 Tesseract는 자동으로 적용합니다. 검사를 위해 중간 이미지를 작성하도록, 즉 내부 이미지 처리가 얼마나 잘 작동하는지 확인하도록 Tesseract에 지시 할 수도 있습니다 ( tessedit_write_images위의 참조에서 검색 ).

더 중요한 것은 Tesseract 4 의 새로운 신경망 시스템 은 일반적으로 특히 노이즈가 많은 이미지에 대해 훨씬 더 나은 OCR 결과를 제공합니다. --oem 1예를 들어 다음과 같이로 활성화 됩니다.

$ tesseract --oem 1 -l deu page.png result pdf

(이 예는 독일어를 선택합니다)

따라서 사용자 정의 전처리 이미지 처리 단계를 적용하기 전에 새로운 Tesseract LSTM 모드로 얼마나 멀리 도달했는지 먼저 테스트하는 것이 좋습니다.


6

조명이 이미지에서 고르지 않은 경우 적응 임계 값이 중요합니다. https://groups.google.com/forum/#!topic/tesseract-ocr/jONGSChLRv4 에서 GraphicsMagic을 사용한 내 전처리가 언급되어 있습니다.

GraphicsMagic에는 곧 시도 할 선형 시간 적응 임계 값에 대한 -lat 기능이 있습니다.

OpenCV를 사용하는 다른 임계 값 방법은 여기에 설명되어 있습니다. http://docs.opencv.org/trunk/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html


2
OpenCV 링크가 변경되었습니다. OpenCV 문서에서 OpenCV-Python Tutorials> OpenCV의 이미지 처리> 이미지 임계 값
richk

2

텍스트가 매우 작지 않은 이미지에서 좋은 결과를 얻으려면이 작업을 수행했습니다.

  1. 원본 이미지에 흐림 효과를 적용합니다.
  2. 적응 임계 값을 적용합니다.
  3. 선명 효과를 적용합니다.

그래도 여전히 좋은 결과를 얻지 못하면 이미지를 150 % 또는 200 %로 조정하십시오.


2

OCR 엔진을 사용하여 이미지 문서에서 텍스트를 읽으려면 정확도를 높이기 위해 많은 문제가 있습니다. 모든 경우에 대한 고정 된 해결책은 없지만 OCR 결과를 개선하기 위해 고려해야 할 몇 가지 사항이 있습니다.

1) 배경 영역의 이미지 품질 저하 / 원치 않는 요소 / 블롭으로 인한 노이즈의 존재 이를 위해서는 가우시안 필터 또는 일반적인 중간 필터 방법을 사용하여 쉽게 수행 할 수있는 노이즈 제거와 같은 일부 전처리 작업이 필요합니다. 이들은 OpenCV에서도 사용 가능합니다.

2) 잘못된 이미지 방향 : 잘못된 방향으로 인해 OCR 엔진은 이미지에서 선과 단어를 올바르게 분할하지 못하여 최악의 정확도를 제공합니다.

3) 라인의 존재 : 단어 또는 라인 세그먼테이션을 수행하는 동안 OCR 엔진은 때때로 단어와 라인을 병합하여 잘못된 내용을 처리하여 잘못된 결과를 제공합니다. 다른 문제들도 있지만 이것들은 기본 문제입니다.

이 사후 OCR 응용 프로그램 은 OCR 결과에 대한 일부 이미지 사전 전처리 및 사후 처리를 적용하여 더 나은 OCR 정확도를 얻을 수있는 사례입니다.


1

텍스트 인식은 우수한 품질의 출력물을 생성하기 위해 다양한 요소에 의존합니다. OCR 출력은 입력 이미지의 품질에 크게 의존합니다. 이것이 모든 OCR 엔진이 입력 이미지의 품질과 크기에 관한 지침을 제공하는 이유입니다. 이 지침은 OCR 엔진이 정확한 결과를 생성하는 데 도움이됩니다.

파이썬에서 이미지 처리에 대한 자세한 기사를 작성했습니다. 자세한 내용은 아래 링크를 참조하십시오. 또한 해당 프로세스를 구현하기 위해 파이썬 소스 코드를 추가했습니다.

개선을 위해이 주제에 대한 제안이나 더 나은 아이디어가 있으면 의견을 작성하십시오.

https://medium.com/cashify-engineering/improve-accuracy-of-ocr-using-image-preprocessing-8df29ec3a033


2
블로그 요약으로 여기에 답변을 추가하십시오. 따라서 링크가 죽어도 대답은 쓸모가 없게됩니다.
Nithin

0

노이즈 감소를 수행 한 다음 임계 값을 적용 할 수 있지만 --psm 및 --oem 값을 변경하여 OCR 구성을 가지고 놀 수 있습니다

시도 : --psm 5 --oem 2

자세한 내용은 다음 링크를 참조 하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.