[image-processing] 테서 랙트 OCR 정확도 향상을위한 이미지 처리

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

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

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



답변

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

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

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


답변

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

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

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

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


답변

이미지의 가독성을 높이기위한 세 가지 점 : 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;
        }

이미지 입력
이미지 입력

출력 이미지
출력 이미지


답변

일반적으로 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 스크립트를 작성하고 문서에서 원하는 것보다 덜 명확했을 때 경험했던 장애물을 정리할 수 있어야합니다.

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


답변

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

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

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


답변

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

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

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

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

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


답변

위의 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;
}