diff --git a/mallinkVideo/pom.xml b/mallinkVideo/pom.xml new file mode 100644 index 000000000..1e706c42f --- /dev/null +++ b/mallinkVideo/pom.xml @@ -0,0 +1,18 @@ + + + + mallink + com.iformall + 1.0 + + 4.0.0 + + mallinkVideo + + + + + + \ No newline at end of file diff --git a/mallinkVideo/src/main/java/com/iformall/video/FormallTess4j.java b/mallinkVideo/src/main/java/com/iformall/video/FormallTess4j.java new file mode 100644 index 000000000..d5912dfb7 --- /dev/null +++ b/mallinkVideo/src/main/java/com/iformall/video/FormallTess4j.java @@ -0,0 +1,290 @@ +package com.iformall.ocr; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; + +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.recognition.software.jdeskew.ImageDeskew; +import net.sourceforge.tess4j.ITessAPI.TessPageIteratorLevel; +import net.sourceforge.tess4j.ITesseract; +import net.sourceforge.tess4j.ITesseract.RenderedFormat; +import net.sourceforge.tess4j.Tesseract; +import net.sourceforge.tess4j.Word; +import net.sourceforge.tess4j.util.ImageHelper; +import net.sourceforge.tess4j.util.LoggHelper; +import net.sourceforge.tess4j.util.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * https://www.cnblogs.com/pejsidney/p/9487881.html + * Tess4J是对Tesseract OCR API.的Java JNA 封装。使java能够通过调用Tess4J的API来使用Tesseract OCR。支持的格式:TIFF,JPEG,GIF,PNG,BMP,JPEG,and PDF + Tesseract 的github地址:https://github.com/tesseract-ocr/tesseract + Tess4J的github地址:https://github.com/nguyenq/tess4j + + Tess4J API 提供的功能: + 1、直接识别支持的文件 + 2、识别图片流 + 3、识别图片的某块区域 + 4、将识别结果保存为 TEXT/ HOCR/ PDF/ UNLV/ BOX + 5、通过设置取词的等级,提取识别出来的文字 + 6、获得每一个识别区域的具体坐标范围 + 7、调整倾斜的图片 + 8、裁剪图片 + 9、调整图片分辨率 + 10、从粘贴板获得图像 + 11、克隆一个图像(目的:创建一份一模一样的图片,与原图在操作修改上,不相 互影响) + 12、图片转换为二进制、黑白图像、灰度图像 + 13、反转图片颜色 + * @author alascor + */ +public class FormallTess4j { + + + private static final Logger logger = LoggerFactory.getLogger(new LoggHelper().toString()); + static final double MINIMUM_DESKEW_THRESHOLD = 0.05d; + + private static final String datapath = "src/main/resources"; + private static final String testResourcesLanguagePath = datapath+"/tessdata"; + private static final String tempfile = "/ocrtempfile/"; + + private static ITesseract instance = new Tesseract(); + + static { + instance.setDatapath(new File(datapath).getPath()); + } + + private static File urlImgToFile(String fileUrl) { + InputStream ins = null; + OutputStream os = null; + try { + URL url = new URL(fileUrl); + URLConnection c = url.openConnection(); + ins = c.getInputStream(); + File folder = new File (tempfile) ; + if (!folder.exists()) { + folder.mkdirs(); + } + File f = new File(tempfile+System.currentTimeMillis()+"-"+IdWorker.get32UUID()+".jpg"); + if (f.exists()) { + f.delete(); + } + f.createNewFile(); + os = new FileOutputStream(f); + int bytesRead = 0; + byte[] buffer = new byte[8192]; + while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.close(); + ins.close(); + return f; + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + }finally { + if (null != os) { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (null != ins) { + try { + ins.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; + } + + private static void deletetempFile(File file) { + if (file.exists()) { + file.delete(); + } + } + + private static String ocrLocalImgFile(String languagePath,String filePath,String fontName) throws Exception { + File imageFile = new File(filePath); + String result = testDoOCR_File(languagePath,imageFile,fontName); + deletetempFile(imageFile); + return result; + } + + public static String ocrNetImgFile(String languagePath,String fileUrl,String fontName) throws Exception { + File imageFile = urlImgToFile(fileUrl); + String result = testDoOCR_File(languagePath,imageFile,fontName); + deletetempFile(imageFile); + return result; + } + + /** + * Test of doOCR method, of class Tesseract. + * 根据图片文件进行识别 + * @throws Exception while processing image. + */ + public static String testDoOCR_File(String languagePath,File imageFile,String fontName) throws Exception { + logger.info("doOCR on a jpg image"); + //set language + instance.setDatapath(languagePath); + instance.setLanguage(fontName); + String result = instance.doOCR(imageFile); + return result; + } + + + public static void main(String[] args) { + try { + //System.out.println(ocrNetImgFile(testResourcesLanguagePath,"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.my0832.com%2Fattachments%2Fbbs%2F20140424%2F201442413083356803_740_1186.jpg&refer=http%3A%2F%2Fimg.my0832.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1613903848&t=985fc4c6c7b7ee82cb98f97ab5bf1459","test1")); + System.out.println(ocrLocalImgFile(testResourcesLanguagePath,"C:\\Users\\iformall\\Desktop\\01.jpg","test1")); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +// +// /** +// * Test of doOCR method, of class Tesseract. +// * 根据图片流进行识别 +// * @throws Exception while processing image. +// */ +// public void testDoOCR_BufferedImage() throws Exception { +// logger.info("doOCR on a buffered image of a PNG"); +// File imageFile = new File(this.testResourcesDataPath, "ocr.png"); +// BufferedImage bi = ImageIO.read(imageFile); +// +// //set language +// instance.setDatapath(testResourcesLanguagePath); +// instance.setLanguage("chi_sim"); +// +// String result = instance.doOCR(bi); +// logger.info(result); +// } +// +// /** +// * Test of getSegmentedRegions method, of class Tesseract. +// * 得到每一个划分区域的具体坐标 +// * @throws java.lang.Exception +// */ +// public void testGetSegmentedRegions() throws Exception { +// logger.info("getSegmentedRegions at given TessPageIteratorLevel"); +// File imageFile = new File(testResourcesDataPath, "ocr.png"); +// BufferedImage bi = ImageIO.read(imageFile); +// int level = TessPageIteratorLevel.RIL_SYMBOL; +// logger.info("PageIteratorLevel: " + Utils.getConstantName(level, TessPageIteratorLevel.class)); +// List result = instance.getSegmentedRegions(bi, level); +// for (int i = 0; i < result.size(); i++) { +// Rectangle rect = result.get(i); +// logger.info(String.format("Box[%d]: x=%d, y=%d, w=%d, h=%d", i, rect.x, rect.y, rect.width, rect.height)); +// } +// +// assertTrue(result.size() > 0); +// } +// +// +// /** +// * Test of doOCR method, of class Tesseract. +// * 根据定义坐标范围进行识别 +// * @throws Exception while processing image. +// */ +// public void testDoOCR_File_Rectangle() throws Exception { +// logger.info("doOCR on a BMP image with bounding rectangle"); +// File imageFile = new File(this.testResourcesDataPath, "ocr.png"); +// //设置语言库 +// instance.setDatapath(testResourcesLanguagePath); +// instance.setLanguage("chi_sim"); +// //划定区域 +// // x,y是以左上角为原点,width和height是以xy为基础 +// Rectangle rect = new Rectangle(84, 21, 15, 13); +// String result = instance.doOCR(imageFile, rect); +// logger.info(result); +// } +// +// /** +// * Test of createDocuments method, of class Tesseract. +// * 存储结果 +// * @throws java.lang.Exception +// */ +// public void testCreateDocuments() throws Exception { +// logger.info("createDocuments for png"); +// File imageFile = new File(this.testResourcesDataPath, "ocr.png"); +// String outputbase = "target/test-classes/docrenderer-2"; +// List formats = new ArrayList(Arrays.asList(RenderedFormat.HOCR, RenderedFormat.TEXT)); +// +// //设置语言库 +// instance.setDatapath(testResourcesLanguagePath); +// instance.setLanguage("chi_sim"); +// +// instance.createDocuments(new String[]{imageFile.getPath()}, new String[]{outputbase}, formats); +// } +// +// /** +// * Test of getWords method, of class Tesseract. +// * 取词方法 +// * @throws java.lang.Exception +// */ +// public void testGetWords() throws Exception { +// logger.info("getWords"); +// File imageFile = new File(this.testResourcesDataPath, "ocr.png"); +// +// //设置语言库 +// instance.setDatapath(testResourcesLanguagePath); +// instance.setLanguage("chi_sim"); +// +// //按照每个字取词 +// int pageIteratorLevel = TessPageIteratorLevel.RIL_SYMBOL; +// logger.info("PageIteratorLevel: " + Utils.getConstantName(pageIteratorLevel, TessPageIteratorLevel.class)); +// BufferedImage bi = ImageIO.read(imageFile); +// List result = instance.getWords(bi, pageIteratorLevel); +// +// //print the complete result +// for (Word word : result) { +// logger.info(word.toString()); +// } +// } +// +// /** +// * Test of Invalid memory access. +// * 处理倾斜 +// * @throws Exception while processing image. +// */ +// public void testDoOCR_SkewedImage() throws Exception { +// //设置语言库 +// instance.setDatapath(testResourcesLanguagePath); +// instance.setLanguage("chi_sim"); +// +// logger.info("doOCR on a skewed PNG image"); +// File imageFile = new File(this.testResourcesDataPath, "ocr_skewed.jpg"); +// BufferedImage bi = ImageIO.read(imageFile); +// ImageDeskew id = new ImageDeskew(bi); +// double imageSkewAngle = id.getSkewAngle(); // determine skew angle +// if ((imageSkewAngle > MINIMUM_DESKEW_THRESHOLD || imageSkewAngle < -(MINIMUM_DESKEW_THRESHOLD))) { +// bi = ImageHelper.rotateImage(bi, -imageSkewAngle); // deskew image +// } +// +// String result = instance.doOCR(bi); +// logger.info(result); +// } + +} diff --git a/mallinkVideo/src/main/java/com/iformall/video/FormallTess4jTrain.java b/mallinkVideo/src/main/java/com/iformall/video/FormallTess4jTrain.java new file mode 100644 index 000000000..d2006fd4c --- /dev/null +++ b/mallinkVideo/src/main/java/com/iformall/video/FormallTess4jTrain.java @@ -0,0 +1,135 @@ +package com.iformall.ocr; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.LineNumberReader; + +/** + * 训练 + * @author iformall + */ +public class FormallTess4jTrain { + + private static final String newFileFolder = "C:\\Users\\iformall\\Desktop\\newfile"; + + private static final String huanhangstr = " \n "; + + private static String getprefix(String lang,String fontName) { + String realFileName = lang+"."+fontName+".exp0"; + return realFileName; + } + + private static String createBoxFile(String tifFileName,String lang,String fontName) { + String prefix = getprefix(lang,fontName); + String realFileName = prefix+".tif"; + if (tifFileName.equals(realFileName)) { + String cmdstr = "tesseract "+tifFileName+" "+prefix+" -l chi_sim batch.nochop makebox"; + return cmdstr; + //String result = executeLocalCmd(cmdstr, null); + + //System.out.println("Done createBoxFile"+result); + }else { + return "文件必须是.tif"; + } + } + + private static String createTranFile(String lang,String fontName) { + String prefix = getprefix(lang,fontName); + String realFileName = prefix+".tif"; + String trainCmdstr = "tesseract "+realFileName+" "+prefix+" nobatch box.train"; + return trainCmdstr; + } + + private static String getBoxFont(String lang,String fontName) { + String prefix = getprefix(lang,fontName); + String extractorCmdstr = "unicharset_extractor "+prefix+".box [other .box files...]"; + return extractorCmdstr; + } + + /** + * 第六步,执行完成后生成以下几个文件加前缀 + * unicharset、inttemp、pffmtable、shapetable、normproto 添加这几个文件的前缀为fontName + * @param lang + * @param fontName + * @return + */ + private static String renameTrainFiles(String fontName) { + StringBuffer sb = new StringBuffer(); + String renCmdstr = "REN unicharset "+fontName+".unicharset"; + sb.append(renCmdstr).append(huanhangstr); + + renCmdstr = "REN inttemp "+fontName+".inttemp"; + sb.append(renCmdstr).append(huanhangstr); + + renCmdstr = "REN pffmtable "+fontName+".pffmtable"; + sb.append(renCmdstr).append(huanhangstr); + + renCmdstr = "REN shapetable "+fontName+".shapetable"; + sb.append(renCmdstr).append(huanhangstr); + + renCmdstr = "REN normproto "+fontName+".normproto"; + sb.append(renCmdstr).append(huanhangstr); + return sb.toString(); + } + + /** + * 第七步:合并5个文件 + * @param lang + * @param fontName + * @return + */ + private static String mergeTrainFiles(String fontName) { + String mergeCmdstr = "combine_tessdata "+fontName+"."; + return mergeCmdstr; + } + + + public static String getTrainSteps(String lang,String fontName) { + + StringBuffer sb = new StringBuffer("请先安装tess4j(4.0以上),配置好环境变量. 下载jTessBoxEditor(用于训练,带FX的支持中文)").append(huanhangstr); + sb.append("请进入到样本图片文件夹用doc命令执行.").append(huanhangstr); + sb.append("第一步,用jTessBoxEditor --> Tools --> Merge 多选图片,保存为tif格式 .命名格式为[lang].[fontName].exp0.tif. 命名:").append(lang).append(".").append(fontName).append(".exp0.tif ").append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第二步,用tif生成box ").append(huanhangstr); + sb.append(createBoxFile(lang+"."+fontName+".exp0.tif", lang, fontName)).append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第三步,用jTessBoxEditor 打开.box文件进行矫正 ").append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第四步:用box文件生成。tr文件 ").append(huanhangstr); + sb.append(createTranFile(lang,fontName)).append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("@@@!!!!从此处开始,可以处理多个字库处理。如原来的字库现在做升级,需要把老的字库的。tif,.box,.tr文件重新命名也放到同目录文件夹, ").append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第五步:提取box字符 ").append(huanhangstr); + sb.append(getBoxFont(lang,fontName)).append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第六步:新建font_properties(没有.txt后缀), 把所有box对应的字体特征加进去。").append(huanhangstr); + sb.append("fontname为字体名称,保持和 图片集文件 .tif 和.box文件的前缀名一致 .的取值为1或0,表示字体是否具有这些属性。 ").append(huanhangstr); + sb.append("如以下内容: ").append(huanhangstr); + sb.append("fontName1 0 0 0 0 0 ").append(huanhangstr); + sb.append("fontName2 0 1 0 0 0 ").append(huanhangstr); + sb.append("fontName3 1 1 0 0 0 ").append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第六步:批量执行系列命令。多个文件在后面累加。 ").append(huanhangstr); + String prefix = getprefix(lang,fontName); + String shapeCmdStr = "### shapeclustering -F font_properties -U unicharset "+prefix+".tr [other .tr files...] "; + sb.append(shapeCmdStr).append(huanhangstr); + String mftraingCmdstr = "mftraining -F font_properties -U unicharset -O unicharset "+prefix+".tr [other .tr files...] "; + sb.append(mftraingCmdstr).append(huanhangstr); + String cntrainCmdstr = "cntraining "+prefix+".tr [other .tr files...] "; + sb.append(cntrainCmdstr).append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第七步,执行完成后生成以下几个文件加前缀: unicharset、inttemp、pffmtable、shapetable、normproto 添加这几个文件的前缀为[fontName] ").append(huanhangstr); + sb.append(renameTrainFiles(fontName)); + sb.append("............ ").append(huanhangstr); + sb.append("第七步:合并5个文件").append(huanhangstr); + sb.append(mergeTrainFiles(fontName)).append(huanhangstr); + sb.append("............ ").append(huanhangstr); + sb.append("第八步:将").append(fontName).append(".traineddata 文件拷贝到后端服务器ocr_data(项目配置fm.ocr_data)目录下。 .tif、。box、。tr文件也需要保留,后面如果这个字库有更新,需要用到这些文件。").append(huanhangstr); + return sb.toString(); + } + +}