1、问题背景:
迁移内网代码,不可以通过U盘等进行数据传输。
2、解决思路:
一开始想的是无非就是拍照片文字识别或者手敲。
但是代码太多了,手敲可以完成,但是时间上和精力上都太折磨了。
一开始用腾讯QQ的ctrl+alt+A的截图来进行文字识别,但是效果不好,很多代码里的大写字母识别出来都是小写字母。
然后在网上搜了搜相关的OCR产品,对比了一下腾讯、阿里、百度的OCR,(谷歌的OCR当时好像是网页打不开,就没细看,有兴趣的同学可以研究研究)
百度的好像没有免费额度,阿里的识别出来的代码会团在一块,没有格式,不分行,腾讯识别出来的有格式,只不过有一些像O,0,o,L,l,1这种字符容易识别错,不过整体来看效果还是很不错的,而且每人每个月有1000条免费识别额度,我用的是通用印刷体(高精度版),这个根据你实际使用情况,因为我要导出的代码里有中文注释,所以用了通用印刷体,如果没有中文或者很少的话,可以用他的英文识别,应该效果会更好一些。
3、迁移步骤。
1、把需要迁移的代码都复制到了Typora的```java代码块内。(复制到哪都无所谓,我主要是为了拍照的时候能够不拍到其他无用的信息)
2、F11进入Typora的全屏模式,进行拍照。
3、把拍好的这些照片按顺序一块发到QQ的我的手机上,点击另存为,新建一个文件夹pic,存到pic中。
这些照片会自动按顺序编号。(试过使用微信的文件传输助手,但是是单张的图片,还要自己另存为,对比QQ太麻烦了。)
4、通过腾讯的API文档,拿到示例代码。
5、改造示例代码,进行OCR,将识别结果输出到控制台。
6、对比内网代码进行部分字符大小写识别有误等问题的修改,完成代码迁移。
4、改造后的代码:贴在文章最后。
这个demo只是单个文件的,我在代码里对图片进行了压缩,循环处理,并对一些识别容易出错的字符进行了判断,有相似需求的可以看看。(图片如果不压缩的话,拍出来的照片会比较大,腾讯的demo代码会报错,我压缩了3倍左右。)
图片参数url和base64我选择了base64.(尝试使用过url,图片不是存储在腾讯云,用的本地的图片,然后通过花生壳进行内网穿透,填的内网穿透之后的那个url地址,但是不好用,所以最终选择base64.)
示例代码中的SecretId,SecretKey参数的值。
点击获取密钥,新建密钥就行了。把这个SecretId和SecretKey放到腾讯那个示例demo里就可以了。
腾讯文字识别计费的参考文档
刚才又去阿里网站看了一下发现有这个全文识别高精版,这个可以成行返回,就是不知道对代码的识别程度怎么样,感兴趣的话可以试试。
阿里的是200次一个月。和腾讯的区别是,腾讯那个是好几种识别服务共用每月1000次免费额度,也可以某个服务用1000次,阿里的这个是某个服务最多200次一个月。
代码。
package com.tencent;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.ocr.v20181119.OcrClient;
import com.tencentcloudapi.ocr.v20181119.models.GeneralAccurateOCRRequest;
import com.tencentcloudapi.ocr.v20181119.models.GeneralAccurateOCRResponse;
import com.tencentcloudapi.ocr.v20181119.models.TextDetection;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Base64;
public class GeneralAccurateOCR {
/**
* 第一次运行之前需要修改的参数如下。
* 1、SecretId,SecretKey.
* 2、OcrClient client = new OcrClient(cred, "ap-beijing", clientProfile);根据自己所在区域选择不同的region参数也就是"ap-beijing"
* <p>
* 每次运行之前需要修改的参数如下。
* 1、for循环的i值,根据实际情况修改起始值和终止值大小。
* 2、修改图片存储路径 savePicPath 和压缩之后输出的路径 compressPicOutPath,压缩之后输出的路径要和图片OCR的路径一致。
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
Credential cred = new Credential("SecretId", "SecretKey");
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("ocr.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
OcrClient client = new OcrClient(cred, "ap-beijing", clientProfile);
// 实例化一个请求对象,每个接口都会对应一个request对象
GeneralAccurateOCRRequest req = new GeneralAccurateOCRRequest();
// 这里想把识别出来的这些代码写到一个文件中,但是不知道为啥不好用,也没仔细研究,
// 因为输出到控制台对我来说效果一样,感兴趣可以研究研究。
// 每个照片都往txt里写一段代码。
String filePath = "path\\pic.txt";
FileWriter writer = new FileWriter(filePath);
for (int i = 2921; i <= 2938; i++) {
// 图片压缩。
// 存放拍照的图片路径
String savePicPath = "path\\pic\\IMG_" + i + ".JPG";
// 存放压缩后的图片路径,也就是准备OCR的图片路径。
String compressPicOutPath = "path\\pic\\" + i + ".JPG";
File sourceFile = new File(savePicPath);
File destinationFile = new File(compressPicOutPath);
float compressionQuality = 0.5f; // 压缩质量范围:0.0f - 1.0f
try {
compressImage(sourceFile, destinationFile, compressionQuality);
System.out.println("第{" + i + "}张图片压缩成功");
} catch (IOException e) {
System.out.println("An error occurred while compressing the image: " + e.getMessage());
}
System.out.println("第{" + i + "}张图片开始OCR");
String fileBase64 = getFileBase64(compressPicOutPath);
req.setImageBase64(fileBase64);
// 返回的resp是一个GeneralAccurateOCRResponse的实例,与请求对象对应
GeneralAccurateOCRResponse resp = client.GeneralAccurateOCR(req);
// 输出json格式的字符串回包
// System.out.println(GeneralAccurateOCRResponse.toJsonString(resp));
// 主要就是拿这个 DetectedText 字段的值。
// {"TextDetections":[{"DetectedText":"@SLf4j","Confidence":100,"Polygon":[{"X":7,"Y":2},{"X":106,"Y":2},{"X":106,"Y":38},{"X":7,"Y":38}],
// "AdvancedInfo":"{\"Parag\":{\"ParagNo\":1}}","ItemPolygon":{"X":7,"Y":2,"Width":100,"Height":37},"Words":[],"WordCoordPoint":[]},
// {"DetectedText":"@Service","Confidence":100,"Polygon":[{"X":8,"Y":44},{"X":135,"Y":42},{"X":136,"Y":75},{"X":9,"Y":77}],
// "AdvancedInfo":"{\"Parag\":{\"ParagNo\":2}}","ItemPolygon":{"X":8,"Y":42,"Width":129,"Height":36},"Words":[],"WordCoordPoint":[]},
TextDetection[] textDetections = resp.getTextDetections();
// 循环逐行写入txt该图片中的所有代码。
for (TextDetection textDetection : textDetections) {
String detectedText = textDetection.getDetectedText();
// 这个前者可以在idea设置里把这个关了,后者就是鼠标光标的识别
if ("no usages new*".equals(detectedText) || "I".equals(detectedText)) {
continue;
}
if (detectedText.contains("Log.")) {
detectedText = detectedText.replace("Log.", "log.");
}
if (detectedText.contains(",")) {
detectedText = detectedText.replace(",", ",");
}
// 这个具体看你代码场景决定是否使用。也就是0特别多还是O特别多。
// if (detectedText.contains("0")) {
// detectedText = detectedText.replace("0", "O");
// }
if (detectedText.contains("@SLf4j")) {
detectedText = detectedText.replace("@SLf4j", "@Slf4j");
}
if (detectedText.contains("@slf4j")) {
detectedText = detectedText.replace("@slf4j", "@Slf4j");
}
if (detectedText.contains("@sTf4j")) {
detectedText = detectedText.replace("@sTf4j", "@Slf4j");
}
if (detectedText.contains("aResource")) {
detectedText = detectedText.replace("aResource", "@Resource");
}
if (detectedText.contains("aOverride")) {
detectedText = detectedText.replace("aOverride", "@Override");
}
if (detectedText.contains("ArnayList")) {
detectedText = detectedText.replace("ArnayList", "ArrayList");
}
if (detectedText.contains("tostring")) {
detectedText = detectedText.replace("tostring", "toString");
}
if (detectedText.contains("v2")) {
detectedText = detectedText.replace("v2", "V2");
}
if (detectedText.contains("setPagesize")) {
detectedText = detectedText.replace("setPagesize", "setPageSize");
}
if (detectedText.contains("Seryice.")) {
detectedText = detectedText.replace("Seryice.", "Service.");
}
if (detectedText.contains("GAutowired")) {
detectedText = detectedText.replace("GAutowired", "@Autowired");
}
System.out.println(detectedText);
writer.write(detectedText + "\n");
}
}
}
/**
* 获取文件的base64
*
* @param filePath
* @return
*/
public static String getFileBase64(String filePath) {
File file = new File(filePath);
try {
// 将文件转换为字节数组
byte[] fileBytes = readBytesFromFile(file);
// 对字节数组进行Base64编码
String base64Data = Base64.getEncoder().encodeToString(fileBytes);
// System.out.println(base64Data);
return base64Data;
// 在这里可以进行后续的操作,如发送Base64数据到服务器等
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static byte[] readBytesFromFile(File file) throws IOException {
FileInputStream fis = null;
byte[] fileBytes;
try {
fis = new FileInputStream(file);
fileBytes = new byte[(int) file.length()];
fis.read(fileBytes);
} finally {
if (fis != null) {
fis.close();
}
}
return fileBytes;
}
/**
* 图片压缩
*
* @param sourceFile
* @param destinationFile
* @param compressionQuality
* @throws IOException
*/
public static void compressImage(File sourceFile, File destinationFile, float compressionQuality) throws IOException {
BufferedImage image = ImageIO.read(sourceFile);
// 获取JPEG图像写入器
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next();
// 创建图像写入参数并设置压缩质量
JPEGImageWriteParam writeParam = new JPEGImageWriteParam(null);
writeParam.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
writeParam.setCompressionQuality(compressionQuality);
// 创建图像输出流并指定目标文件
ImageOutputStream outputStream = ImageIO.createImageOutputStream(destinationFile);
writer.setOutput(outputStream);
// 写入图像并完成压缩
writer.write(null, new javax.imageio.IIOImage(image, null, null), writeParam);
writer.dispose();
outputStream.close();
}
}