You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2021/01/05 19:43:56 UTC

[tika] branch main updated: TIKA-3259 -- improve logging in TesseractOCRParser

This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new 6c998ff  TIKA-3259 -- improve logging in TesseractOCRParser
6c998ff is described below

commit 6c998ff0dd8162dca968ea2bbf779b82bbdfd21f
Author: tallison <ta...@apache.org>
AuthorDate: Tue Jan 5 14:43:29 2021 -0500

    TIKA-3259 -- improve logging in TesseractOCRParser
---
 .../apache/tika/parser/ocr/TesseractOCRParser.java | 84 ++++++++++++++++------
 .../tika/parser/ocr/TesseractOCRParserTest.java    | 22 +++---
 2 files changed, 73 insertions(+), 33 deletions(-)

diff --git a/tika-parsers/tika-parsers-classic/tika-parsers-classic-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java b/tika-parsers/tika-parsers-classic/tika-parsers-classic-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java
index ff2ea2f..24f6dd8 100644
--- a/tika-parsers/tika-parsers-classic/tika-parsers-classic-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java
+++ b/tika-parsers/tika-parsers-classic/tika-parsers-classic-modules/tika-parser-ocr-module/src/main/java/org/apache/tika/parser/ocr/TesseractOCRParser.java
@@ -161,18 +161,22 @@ public class TesseractOCRParser extends AbstractParser implements Initializable
         if (! config.getTesseractPath().isEmpty() &&
                 ! Files.isDirectory(Paths.get(config.getTesseractPath()))) {
             TESSERACT_PRESENT.put(tesseract, false);
+            LOG.warn("You haven't specified an existing directory in " +
+                    "which the tesseract binary should be found: " +
+                    "(path:" + config.getTesseractPath()+")");
             return false;
         }
 
         // Try running Tesseract from there, and see if it exists + works
         String[] checkCmd = { tesseract };
         boolean hasTesseract = ExternalParser.check(checkCmd);
+        LOG.debug("hasTesseract (path: "+checkCmd+"): "+hasTesseract);
         TESSERACT_PRESENT.put(tesseract, hasTesseract);
         return hasTesseract;
      
     }
     
-    private boolean hasImageMagick(TesseractOCRConfig config) {
+    public static boolean hasImageMagick(TesseractOCRConfig config) {
         // Fetch where the config says to find ImageMagick Program
         String ImageMagick = getImageMagickPath(config);
 
@@ -194,6 +198,10 @@ public class TesseractOCRParser extends AbstractParser implements Initializable
         // Try running ImageMagick program from there, and see if it exists + works
         String[] checkCmd = { ImageMagick };
         boolean hasImageMagick = ExternalParser.check(checkCmd);
+        if (!hasImageMagick) {
+            LOG.warn("ImageMagick does not appear to be installed " +
+                    "(commandline: "+ImageMagick+")");
+        }
         IMAGE_MAGICK_PRESENT.put(ImageMagick, hasImageMagick);
         
         return hasImageMagick;
@@ -201,7 +209,7 @@ public class TesseractOCRParser extends AbstractParser implements Initializable
     }
 
 
-    private String getImageMagickPath(TesseractOCRConfig config) {
+    private static String getImageMagickPath(TesseractOCRConfig config) {
         return config.getImageMagickPath() + getImageMagickProg();
     }
 
@@ -227,30 +235,58 @@ public class TesseractOCRParser extends AbstractParser implements Initializable
 
         // check if python is installed and it has the required dependencies for the rotation program to run
         boolean hasPython = false;
+
+        String[] checkCmd = { pythonPath, "--version" };
+        boolean hasPythonExecutable = ExternalParser.check(checkCmd);
+        if (! hasPythonExecutable) {
+            LOG.warn("couldn't run python executable ("+
+                    pythonPath+")");
+            PYTHON_PRESENT.put(pythonPath, hasPythonExecutable);
+            return hasPythonExecutable;
+        }
+
         TemporaryResources tmp = null;
+        File importCheck = null;
         try {
             tmp = new TemporaryResources();
-            File importCheck = tmp.createTemporaryFile();
+            importCheck = tmp.createTemporaryFile();
             String prg = "from skimage.transform import radon\n" +
-                    "from PIL import Image\n"+"" +
+                    "from PIL import Image\n" + "" +
                     "import numpy\n";
             OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(importCheck), Charset.forName("UTF-8"));
             out.write(prg);
+            out.flush();
             out.close();
+        } catch (IOException e) {
+            LOG.warn("Error writing file to test correct libs are available", e);
+            hasPython = false;
+            PYTHON_PRESENT.put(pythonPath, hasPython);
+            return hasPython;
+        }
 
-            Process p = Runtime.getRuntime().exec(new String[]{
-                    pythonPath,
-                    ProcessUtils.escapeCommandLine(importCheck.getAbsolutePath())});
-            if (p.waitFor() == 0) {
-                hasPython = true;
+        Process p = null;
+            try {
+                p = Runtime.getRuntime().exec(new String[]{
+                        pythonPath,
+                        ProcessUtils.escapeCommandLine(importCheck.getAbsolutePath())});
+                boolean completed = p.waitFor(30, TimeUnit.SECONDS);
+                hasPython = completed;
+                if (! completed) {
+                    LOG.warn("python3 did not successfully complete after 30 seconds");
+                    LOG.warn("rotation.py cannot be called");
+                }
+            } catch (SecurityException e) {
+                throw e;
+            } catch (Exception e) {
+                LOG.warn("python3 ("+
+                                pythonPath+ ") is not installed with the required dependencies: scikit-image and numpy",
+                        e);
+            } finally {
+                if (p != null) {
+                    p.destroyForcibly();
+                }
+                IOUtils.closeQuietly(tmp);
             }
-        } catch (SecurityException e) {
-            throw e;
-        } catch (Exception e) {
-            //swallow
-        } finally {
-            IOUtils.closeQuietly(tmp);
-        }
         PYTHON_PRESENT.put(pythonPath, hasPython);
         return hasPython;
     }
@@ -390,14 +426,17 @@ public class TesseractOCRParser extends AbstractParser implements Initializable
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
             PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
             executor.setStreamHandler(streamHandler);
+            String tmpAngle = "";
             try {
                 executor.execute(commandLine);
-                String tmpAngle = outputStream.toString("UTF-8").trim();
+                tmpAngle = outputStream.toString("UTF-8").trim();
                 //verify that you've gotten a numeric value out
                 Double.parseDouble(tmpAngle);
                 angle = tmpAngle;
-            } catch(Exception e) {	
-                //TODO: log
+            } catch(SecurityException e) {
+                throw e;
+            } catch (Exception e) {
+                LOG.warn("rotation.py failed (commandline: "+commandLine+") tmpAngle: "+tmpAngle, e);
             }
         }
               
@@ -417,11 +456,12 @@ public class TesseractOCRParser extends AbstractParser implements Initializable
                 scratchFile.getAbsolutePath()
         };
         commandLine.addArguments(args, true);
-        LOG.debug("ImageMagick commandline: "+commandLine);
 		try {
 			executor.execute(commandLine);
-		} catch(Exception e) {	
-            //TODO: log
+		} catch (SecurityException e) {
+		    throw e;
+        } catch (Exception e) {
+            LOG.warn("ImageMagick failed (commandline: "+commandLine+")", e);
 		} 
        
         tmp.close();
diff --git a/tika-parsers/tika-parsers-classic/tika-parsers-classic-package/src/test/java/org/apache/tika/parser/ocr/TesseractOCRParserTest.java b/tika-parsers/tika-parsers-classic/tika-parsers-classic-package/src/test/java/org/apache/tika/parser/ocr/TesseractOCRParserTest.java
index 3f1feb9..d206ed6 100644
--- a/tika-parsers/tika-parsers-classic/tika-parsers-classic-package/src/test/java/org/apache/tika/parser/ocr/TesseractOCRParserTest.java
+++ b/tika-parsers/tika-parsers-classic/tika-parsers-classic-package/src/test/java/org/apache/tika/parser/ocr/TesseractOCRParserTest.java
@@ -290,17 +290,17 @@ public class TesseractOCRParserTest extends TikaTest {
     @Test
     public void testRotatedOCR() throws Exception {
         TesseractOCRConfig config = new TesseractOCRConfig();
-        if (TesseractOCRParser.hasPython(config)) {
-            config.setApplyRotation(true);
-            config.setEnableImageProcessing(1);
-            config.setResize(100);
-            ParseContext parseContext = new ParseContext();
-            parseContext.set(TesseractOCRConfig.class, config);
-            assumeTrue(canRun(config));
-
-            String ocr = getText("testRotated.png", new Metadata(), parseContext);
-            assertContains("Its had resolving otherwise she contented therefore", ocr);
-        }
+        assumeTrue(TesseractOCRParser.hasPython(config));
+        assumeTrue(TesseractOCRParser.hasImageMagick(config));
+        config.setApplyRotation(true);
+        config.setEnableImageProcessing(1);
+        config.setResize(100);
+        ParseContext parseContext = new ParseContext();
+        parseContext.set(TesseractOCRConfig.class, config);
+        assumeTrue(canRun(config));
+
+        String ocr = getText("testRotated.png", new Metadata(), parseContext);
+        assertContains("Its had resolving otherwise she contented therefore", ocr);
     }
 
     @Test