OCR的另類使用。你相信OCR可以進行圖片修復嗎?不管信不信。反正小帥是這樣做了一個案例。
接下來就緊跟步伐看小帥是如何實現這樣的功能吧
實現步驟
Step1:成爲百度AI開放平臺的開發者
我們有賬號之後登錄,並且點擊此處(文字識別)創建一個應用,如下圖
然後就能看到創建完的應用和 APPID、API KEY 以及 Secret KEY了
由於圖像修復是另一個模塊下的接口,爲了不創建過多的應用。咱們開發者需要記住APPID。申請加入百度圖像識別官方QQ羣(羣號:659268104),提供公司名稱、APPID、應用場景,等待百度工作人員協助開通權限後方可使用。
Step2:準備數據
文字識別服務可以讓小帥把自己拍攝的含有文字的圖片轉化成文本數據,然後就可以對文字進行編輯等操作,咱們需要藉助於OCR部分數據進行圖片修復。那小帥提前準備的圖片如下
Step3: 編寫一個文字識別示例程序
有 第一步 的 API KEY 以及 Secret KEY,以及 第二步 的數據,我們就可以寫一個示例代碼調用百度AI開放平臺的文字識別能力
準備開發環境
小帥選擇用 Java來快速搭建一個原型,關於如何安裝Java。可以參考百度經驗哦~。百度AI有很完善的API文檔、和封裝調用更方便的工具包。接下來小帥就用Maven搭建工程環境
pom.xml配置如下:
<!-- https://mvnrepository.com/artifact/com.baidu.aip/java-sdk -->
<dependency>
<groupId>com.baidu.aip</groupId>
<artifactId>java-sdk</artifactId>
<version>4.12.0</version>
</dependency>
編寫代碼
粘貼以下內容,不要忘記替換你的 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<String, String>());
// 格式化輸出接口識別數據
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處理器。
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
序列化後的對象以及代碼改動
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<WordsResultBean> 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;//表示位置的長方形的高度
}
}
}
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<String, String>());
// 格式化輸出接口識別數據
//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<String, String>());
System.out.println(res.toString());
long endTime = System.currentTimeMillis();
System.out.println("初始化到返回結果耗時 "+(endTime - startTime));
System.out.println("調用接口到返回結果耗時 "+(endTime - startTime2));
初始化到返回結果耗時 1197
調用接口到返回結果耗時 1172
初始化到返回結果耗時 758
調用接口到返回結果耗時 733
以上數據可以看出。平均耗時在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;
/**
* 邀測也封裝在工具包 方便快速調用
*
* @author xiaoshuai
*/
public class AipImageProcessPro extends AipImageProcess {
//圖像修復
static final String INPAINTING = "https://aip.baidubce.com/rest/2.0/image-process/v1/inpainting";
public AipImageProcessPro(String appId, String apiKey, String secretKey) {
super(appId, apiKey, secretKey);
}
/**
* 圖像修復接口
* 去除圖片中不需要的遮擋物,並用背景內容填充,提高圖像質量。
*
* @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<String, Object> 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<String, Object> 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<String, Object> options = new HashMap<String, Object>();
//rectangle 支持多個
List<Object> rectangle = new ArrayList<Object>();
//rectangle包含的left, top, width, height
Map<String, Object> rectangleMap = new HashMap<String, Object>();
// 文字接口調用返回的位置信息
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<String, Object> options = new HashMap<String, Object>();
//rectangle 支持多個
List<Object> rectangle = new ArrayList<Object>();
//rectangle包含的left, top, width, height
Map<String, Object> rectangleMap = new HashMap<String, Object>();
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<String, String>());
OCRBean bean = JSON.parseObject(res.toString(),OCRBean.class);
//rectangle 要去除的位置爲規則矩形時,給出座標信息,每個元素包含left, top, width, height,int 類型。
String inpainting_type = "rectangle";
//參數組裝
HashMap<String, Object> options = new HashMap<String, Object>();
//rectangle 支持多個
List<Object> rectangle = new ArrayList<Object>();
//rectangle包含的left, top, width, height
Map<String, Object> rectangleMap = new HashMap<String, Object>();
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);
}
}