【通用文字识别另类使用】进行图像修复
作者:756665228
OCR的另类使用。你相信OCR可以进行图片修复吗?不管信不信。反正小帅是这样做了一个案例。
接下来就紧跟步伐看小帅是如何实现这样的功能吧
实现步骤
我们有账号之后登录,并且点击此处(文字识别)创建一个应用,如下图
然后就能看到创建完的应用和 APPID、API KEY 以及 Secret KEY了
由于图像修复是另一个模块下的接口,为了不创建过多的应用。咱们开发者需要记住APPID。申请加入百度图像识别官方QQ群(群号:659268104),提供公司名称、APPID、应用场景,等待百度工作人员协助开通权限后方可使用。
Step2:准备数据
文字识别服务可以让小帅把自己拍摄的含有文字的图片转化成文本数据,然后就可以对文字进行编辑等操作,咱们需要借助于OCR部分数据进行图片修复。那小帅提前准备的图片如下
Step3: 编写一个文字识别示例程序
有 第一步 的 API KEY 以及 Secret KEY,以及 第二步 的数据,我们就可以写一个示例代码调用百度AI开放平台的文字识别能力
准备开发环境
小帅选择用 Java来快速搭建一个原型,关于如何安装Java。可以参考百度经验哦~。百度AI有很完善的API文档、和封装调用更方便的工具包。接下来小帅就用Maven搭建工程环境
pom.xml配置:
由于社区html标签会被解析。这里只给出地址。开发者们自行访问复制pom的maven配置内容即可
编写代码
粘贴以下内容,不要忘记替换你的 APPID APIKEY 以及 SECRETKEY 和 图片文件
import com.baidu.aip.ocr.AipOcr;
import org.json.JSONObject;
import java.util.HashMap;
public class Sample {
//第一步创建应用获取的三个值
private static String APPID = “你的 App ID”;
private static String APIKEY = “你的 Api Key”;
private static String SECRETKEY = “你的 Secret Key”;
public static void main(String[] args) {
// 初始化一个AipOcr
AipOcr client = new AipOcr(APPID,APIKEY,SECRETKEY);
// 调用接口 第二步准备的图片
String path = "/Users/xiaoshuai/Downloads/bolg/demo.jpg";
// 使用带位置信息的接口哦 因为图像修复需要用到哦 图片数据SDK只支持本地图片 或 本地图片的byte[]
JSONObject res = client.accurateGeneral(path, new HashMap());
// 格式化输出接口识别数据
System.out.println(res.toString(2));
}
}
保存接口返回的内容。可以看到百度OCR精准度很高哦~
{
“log_id”: 80968705********9307,
“words_result”: [{
“words”: " BattleBet",
“location”: {
“top”: 128,
“left”: 509,
“width”: 264,
“height”: 51
}
}],
“words_result_num”: 1
}
对数据进行序列化处理,方便后续操作。需要对pom.xml做如下改变
Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率。例如开发中经常需要写的javabean,都需要花时间去添加相应的getter/setter,也许还要去写构造器、equals等方法,而且需要维护,当属性多时会出现大量的getter/setter方法,这些显得很冗长也没有太多技术含量,一旦修改属性,就容易出现忘记修改对应方法的失误。
Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。出现的神奇就是在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁些。
Fastjson是阿里巴巴公司开源的速度最快的Json和对象转换工具,一个Java语言编写的JSON处理器。
由于社区html标签会被解析。这里只给出地址。开发者们自行访问复制pom的maven配置内容即可
序列化后的对象以及代码改动
import java.util.List;
@lombok.NoArgsConstructor
@lombok.Data
public class OCRBean {
/**
* log_id : 8096870549762079307
* words_result : [{"words":" BattleBet","location":{"top":128,"left":509,"width":264,"height":51}}]
* words_result_num : 1
*/
private long log_id;//唯一的log id,用于问题定位
private int words_result_num;//识别结果数,表示words_result的元素个数
private List words_result;//定位和识别结果数组
@lombok.NoArgsConstructor
@lombok.Data
public static class WordsResultBean {
/**
* words : BattleBet
* location : {"top":128,"left":509,"width":264,"height":51}
*/
private String words;//识别结果字符串
private LocationBean location;//位置数组(坐标0点为左上角)
@lombok.NoArgsConstructor
@lombok.Data
public static class LocationBean {
/**
* top : 128
* left : 509
* width : 264
* height : 51
*/
private int top;//表示定位位置的长方形左上顶点的垂直坐标
private int left;//表示定位位置的长方形左上顶点的水平坐标
private int width;//表示定位定位位置的长方形的宽度
private int height;//表示位置的长方形的高度
}
}
}
Java序列化调用一次 并输出文字位置内容
import com.alibaba.fastjson.JSON;
import com.baidu.aip.ocr.AipOcr;
import org.json.JSONObject;
import java.util.HashMap;
public class Sample {
//第一步创建应用获取的三个值
private static String APPID = “你的 App ID”;
private static String APIKEY = “你的 Api Key”;
private static String SECRETKEY = “你的 Secret Key”;
public static void main(String[] args) {
// 初始化一个AipOcr
AipOcr client = new AipOcr(APPID,APIKEY,SECRETKEY);
// 调用接口 第二步准备的图片
String path = "/Users/xiaoshuai/Downloads/bolg/demo.jpg";
// 使用带位置信息的接口哦 因为图像修复需要用到哦 图片数据SDK只支持本地图片 或 本地图片的byte[]
JSONObject res = client.accurateGeneral(path, new HashMap());
// 格式化输出接口识别数据
//System.out.println(res.toString(2));
//序列化并输出位置信息
OCRBean bean = JSON.parseObject(res.toString(),OCRBean.class);
System.out.println(bean.getWords_result().get(0).getLocation().getHeight());
System.out.println(bean.getWords_result().get(0).getLocation().getWidth());
System.out.println(bean.getWords_result().get(0).getLocation().getLeft());
System.out.println(bean.getWords_result().get(0).getLocation().getTop());
}
}
结果输出如下
51
264
509
128
文字识别调用到此咱们就算是中高级掌握了哦。(调用接口、序列化输出) 。有木有发现很简单
文字识别SDK 非单例加载。耗时如下
//代码的修改
long startTime = System.currentTimeMillis();
// 初始化一个AipOcr
AipOcr client = new AipOcr(APPID,APIKEY,SECRETKEY);
// 调用接口 第二步准备的图片
String path = “/Users/xiaoshuai/Downloads/bolg/demo.jpg”;
// 使用带位置信息的接口哦 因为图像修复需要用到哦 图片数据SDK只支持本地图片 或 本地图片的byte[]
long startTime2 = System.currentTimeMillis();
JSONObject res = client.accurateGeneral(path, new HashMap());
System.out.println(res.toString());
long endTime = System.currentTimeMillis();
System.out.println("初始化到返回结果耗时 "+(endTime - startTime));
System.out.println("调用接口到返回结果耗时 "+(endTime - startTime2));
初始化到返回结果耗时 1197
调用接口到返回结果耗时 1172
初始化到返回结果耗时 758
调用接口到返回结果耗时 733
小帅的硬件配置情况。带宽是联通100M
以上数据可以看出。平均耗时在1s上下。如果单例加载SDK,服务器配置也贼6、带宽也贼宽。应该耗时还会更低哦
Step4: 图像修复搞起来
环境与第三步相同即可。需要注意 确保创建的应用 已经与百度相关群沟通并取得图像修复接口权限哦
https://ai.baidu.com/docs#/ImageProcessing-API/9ad81fba 官方接口文档一定要看哦。不然不清楚参数形式哦
下面示例使用 rectangle 参数进行图像修复 要去除的位置为规则矩形
那修复图片中不规则的部分怎么办?不仅仅是文字怎么办?
不怕 接口 提供了 要去除的位置不规则 的形式
mask 输入的mask文件只能是png格式的黑白图片,缺损部位用0(黑色)表示,完好部分用255(白色)表示,不能有0、255之外的其他值。mask的大小和image大小必须一致。
由于邀测接口不会直接封装在工具包中。咱们优先自己封装在SDK中。代码如下
import com.baidu.aip.error.AipError;
import com.baidu.aip.http.AipRequest;
import com.baidu.aip.http.EBodyFormat;
import com.baidu.aip.http.Headers;
import com.baidu.aip.http.HttpContentType;
import com.baidu.aip.imageprocess.AipImageProcess;
import com.baidu.aip.util.Base64Util;
import com.baidu.aip.util.Util;
import org.json.JSONObject;
import java.io.IOException;
import java.util.HashMap;
/**
图像修复接口
去除图片中不需要的遮挡物,并用背景内容填充,提高图像质量。
@param image - 二进制图像数据
@param inpainting_type - 选择修复类型的参数,“mask“和” rectangle“二选一。
@param options - 可选必选参数对象,key: value都为string类型
options - options列表:
mask和rectangle二选一
mask 要去除的位置不规则时,上传mask图片(base64编码)。输入的mask文件只能是png格式的黑白图片,缺损部位用0(黑色)表示,完好部分用255(白色)表示,不能有0、255之外的其他值。mask的大小和image大小必须一致。
rectangle 要去除的位置为规则矩形时,给出坐标信息,每个元素包含left, top, width, height,int 类型。
@return JSONObject
*/
public JSONObject inpainting(byte[] image, String inpainting_type, HashMap options) {
AipRequest request = new AipRequest();
preOperation(request);
String base64Content = Base64Util.encode(image);
request.addBody(“image”, base64Content);
request.addBody(“inpainting_type”, inpainting_type);
if (options != null) {
request.addBody(options);
}
request.setUri(INPAINTING);
request.addHeader(Headers.CONTENT_TYPE, HttpContentType.JSON_DATA);
request.setBodyFormat(EBodyFormat.RAW_JSON);
System.out.println(request.getBodyStr());
postOperation(request);
return requestServer(request);
}
/**
图像修复接口
去除图片中不需要的遮挡物,并用背景内容填充,提高图像质量。
@param image - 本地图片路径
@param inpainting_type - 选择修复类型的参数,“mask“和” rectangle“二选一。
@param options - 可选必选参数对象,key: value都为string类型
options - options列表:
mask和rectangle二选一
mask 要去除的位置不规则时,上传mask图片(base64编码)。输入的mask文件只能是png格式的黑白图片,缺损部位用0(黑色)表示,完好部分用255(白色)表示,不能有0、255之外的其他值。mask的大小和image大小必须一致。
rectangle 要去除的位置为规则矩形时,给出坐标信息,每个元素包含left, top, width, height,int 类型。
@return JSONObject
*/
public JSONObject inpainting(String image, String inpainting_type,HashMap options) {
try {
byte[] data = Util.readFileByBytes(image);
return inpainting(data,inpainting_type,options);
} catch (IOException e) {
e.printStackTrace();
return AipError.IMAGE_READ_ERROR.toJsonResult();
}
}
}
调用接口示例代码
输出为图片的base64 内容过多 这里就不给出了。
2919-11-12 22:22多 注意:接口返回的key只有log_id 和 image 并不是接口文档说明的rect_image 或 mask_image
或许各位开发者在调用的时候,接口已经全量同步于接口文档说明一致的参数哦
import org.json.JSONObject;
import java.util.*;
public class SampleImgProcess {
//第一步创建应用获取的三个值
private static String APPID = “你的 App ID”;
private static String APIKEY = “你的 Api Key”;
private static String SECRETKEY = “你的 Secret Key”;
public static void main(String[] args) {
// 初始化一个AipOcr
AipImageProcessPro client = new AipImageProcessPro(APPID,APIKEY,SECRETKEY);
// 调用接口 第二步准备的图片
String path = “/Users/xiaoshuai/Downloads/bolg/demo.jpg”;
//rectangle 要去除的位置为规则矩形时,给出坐标信息,每个元素包含left, top, width, height,int 类型。
String inpainting_type = “rectangle”;
//参数组装
HashMap options = new HashMap();
//rectangle 支持多个
List rectangle = new ArrayList();
//rectangle包含的left, top, width, height
Map rectangleMap = new HashMap();
// 文字接口调用返回的位置信息
rectangleMap.put(“top”, 128);
rectangleMap.put(“left”, 509);
rectangleMap.put(“width”, 264);
rectangleMap.put(“height”, 51);
rectangle.add(rectangleMap);
options.put(“rectangle”, rectangle);
// 图片数据SDK只支持本地图片 或 本地图片的byte[]
JSONObject res = client.inpainting(path, inpainting_type,options);
// 格式化输出接口返回的内容
System.out.println(res.toString());
}
}
老套路还是序列化一下。对接口返回的图片的base64数据进行转存为图片文件。并进行查看效果
以下代码还会增加一个base64处理的方法。仅供参考。并非必须那样写才行哦 切记JDK1.8+
import com.alibaba.fastjson.JSON;
import org.json.JSONObject;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.*;
public class SampleImgProcess {
//第一步创建应用获取的三个值
private static String APPID = “你的 App ID”;
private static String APIKEY = “你的 Api Key”;
private static String SECRETKEY = “你的 Secret Key”;
public static void main(String[] args) throws Exception{
// 初始化一个AipImageProcessPro
AipImageProcessPro client = new AipImageProcessPro(APPID,APIKEY,SECRETKEY);
// 调用接口 第二步准备的图片
String path = “/Users/xiaoshuai/Downloads/bolg/demo.jpg”;
//rectangle 要去除的位置为规则矩形时,给出坐标信息,每个元素包含left, top, width, height,int 类型。
String inpainting_type = “rectangle”;
//参数组装
HashMap options = new HashMap();
//rectangle 支持多个
List rectangle = new ArrayList();
//rectangle包含的left, top, width, height
Map rectangleMap = new HashMap();
rectangleMap.put(“top”, 128);
rectangleMap.put(“left”, 509);
rectangleMap.put(“width”, 264);
rectangleMap.put(“height”, 51);
rectangle.add(rectangleMap);
options.put(“rectangle”, rectangle);
// 图片数据SDK只支持本地图片 或 本地图片的byte[]
JSONObject res = client.inpainting(path, inpainting_type,options);
// 格式化输出接口返回的内容
//System.out.println(res.toString());
ImageProcessBean bean = JSON.parseObject(res.toString(),ImageProcessBean.class);
//获取处理后的图片的base64 进行另存为图片文件处理
saveImage(bean.getImage());
}
/**
* 把处理后到image保存为图片文件
*
* @param image 图片的base64
* @throws Exception
*/
private static void saveImage(String image) throws Exception{
byte[] bytes = Base64.getDecoder().decode(image);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
BufferedImage bufferedImage = ImageIO.read(bais);
// 图片保存路径
String path = "/Users/xiaoshuai/Downloads/bolg/demonew.jpg";
File file = new File(path);
ImageIO.write(bufferedImage,"jpg",file);
}
}
看一下处理后的图片
有木有发现貌似图片压根就没有过 BattleBet 文字似的。
细心的开发者会发现。前面的图标还存在。图标不属于文字。所以文字识别返回的位置信息就不会包含那个图标了。
只处理包含文字的图片还是轻而易举的哦~
Step5: 图像修复文字识别结合一下
结合一下的代码如下
总耗时 3115 到IO保存图片文件完成 耗时在3s上下 这个速度应该比PS新手快多了吧 嘿嘿
package cn.ydxiaoshuai.sample;
import com.alibaba.fastjson.JSON;
import com.baidu.aip.ocr.AipOcr;
import org.json.JSONObject;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.*;
public class MergeSample {
//第一步创建应用获取的三个值
private static String APPID = “你的 App ID”;
private static String APIKEY = “你的 Api Key”;
private static String SECRETKEY = “你的 Secret Key”;
public static void main(String[] args) throws Exception{
long startTime = System.currentTimeMillis();
// 初始化一个AipOcr
AipOcr client = new AipOcr(APPID,APIKEY,SECRETKEY);
// 初始化一个AipImageProcessPro
AipImageProcessPro aipImageProcessPro = new AipImageProcessPro(APPID,APIKEY,SECRETKEY);
// 初始化AipOcr AipImageProcessPro 其实可以自行修改SDK代码 只需初始化一个即可 这样也可以降低耗时哦~
// 调用接口 第二步准备的图片
String path = "/Users/xiaoshuai/Downloads/bolg/demo.jpg";
// 先调用文字识别 获取文字位置信息
JSONObject res = client.accurateGeneral(path, new HashMap());
OCRBean bean = JSON.parseObject(res.toString(),OCRBean.class);
//rectangle 要去除的位置为规则矩形时,给出坐标信息,每个元素包含left, top, width, height,int 类型。
String inpainting_type = "rectangle";
//参数组装
HashMap options = new HashMap();
//rectangle 支持多个
List rectangle = new ArrayList();
//rectangle包含的left, top, width, height
Map rectangleMap = new HashMap();
rectangleMap.put("top", bean.getWords_result().get(0).getLocation().getTop());
rectangleMap.put("left", bean.getWords_result().get(0).getLocation().getLeft());
rectangleMap.put("width", bean.getWords_result().get(0).getLocation().getWidth());
rectangleMap.put("height", bean.getWords_result().get(0).getLocation().getHeight());
rectangle.add(rectangleMap);
options.put("rectangle", rectangle);
// 图片数据SDK只支持本地图片 或 本地图片的byte[]
JSONObject result = aipImageProcessPro.inpainting(path, inpainting_type,options);
// 格式化输出接口返回的内容
//System.out.println(res.toString());
ImageProcessBean imageProcessBean = JSON.parseObject(result.toString(),ImageProcessBean.class);
saveImage(imageProcessBean.getImage());
long endTime = System.currentTimeMillis();
System.out.println("总耗时 "+(endTime - startTime));
}
/**
* 把处理后到image保存为图片文件
*
* @param image 图片的base64
* @throws Exception
*/
private static void saveImage(String image) throws Exception{
byte[] bytes = Base64.getDecoder().decode(image);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
BufferedImage bufferedImage = ImageIO.read(bais);
// 图片保存露肩
String path = "/Users/xiaoshuai/Downloads/bolg/demonew.jpg";
File file = new File(path);
ImageIO.write(bufferedImage,"jpg",file);
}
}