談談調用騰訊雲【OCR-通用印刷體識別】Api踩的坑

1、寫在前面

最近作項目須要用到識別圖片中文字的功能,原本用的Tesseract這個寫的,不過效果不是很理想。html

隨後上網搜了一下OCR接口,就準備使用騰訊雲、百度的OCR接口試一下效果。不過這個騰訊雲OCR就折騰了一天!git

2、OCR-通用印刷體識別

首先附上文檔地址:OCR-通用印刷體識別github

一、通用OCR簡介

通用OCR技術提供圖片總體文字的檢測和識別服務,返回文字框位置與文字內容。支持多場景、任意版面下整圖文字的識別,以及中英文、字母、數字的識別。被普遍應用於印刷文檔識別、廣告圖文字識別、街景店招識別、菜單識別、視頻標題識別、互聯網頭像文字識別等。web

二、接口url地址

http://recognition.image.myqcloud.com/ocr/general算法

三、請求包header

接口採用http協議,支持指定圖片URL和上傳本地圖片文件兩種方式。json

全部請求都要求含有下列的頭部信息:app

Host、Content-Length、Content-Type、Authorizationide

3、示例

一、使用 url 的請求包

POST /ocr/general HTTP/1.1
Authorization: FCHXdPTEwMDAwMzc5Jms9QUtJRGVRZDBrRU1yM2J4ZjhRckJi==
Host: recognition.image.myqcloud.com
Content-Length: 187
Content-Type: application/json
{
  "appid":"123456",
  "bucket":"test",
  "url":"http://test-123456.image.myqcloud.com/test.jpg"
} 

這個url是萬象優圖中圖片的url地址。post

看到這種格式,內心大概有數了,開始敲代碼。ui

二、具體實現

HttpClient client = new HttpClient();
var para = new
{
    appid = "123456",
    bucket = "test",
    url = "http://test-123456.image.myqcloud.com/test.jpg"
};
var jsonPara = JsonConvert.SerializeObject(para);
StringContent content = new StringContent(jsonPara);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentLength = jsonPara.Length;
content.Headers.Add("Host", "recognition.image.myqcloud.com");
content.Headers.Add("Authorization", aut);
var taskHrm = client.PostAsync(postUrl, content);
taskHrm.Wait();
var taskStr = taskHrm.Result.Content.ReadAsStringAsync();
taskStr.Wait();
var result = taskStr.Result;

F5運行,結果報錯了:

這個錯誤的緣由爲:Host和Authorization不能這樣添加到Headers中。因而乎我自做聰明的把它們放到參數中了:

var para = new
{
    appid = "123456",
    bucket = "test",
    url = "http://test-123456.image.myqcloud.com/test.jpg",
    Host = "recognition.image.myqcloud.com",
    Authorization = aut
};   

這樣運行代碼是沒有報錯,不事後臺返回「has no sign or sign is empty」,沒有簽名。

再回頭看看參數要求,Host和Authorization必須添加在請求包Header中。因而百度一下,如何使用Httpclient設置Authorization。最終解決方案以下:

client.DefaultRequestHeaders.Host = "recognition.image.myqcloud.com";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", aut); 

成功運行!

可是項目中使用url的概率畢竟少,基本上都是上傳一張本地圖片,而後識別出來文字。

下面來看一下識別一張本地圖片。

三、使用 image 的請求包

POST /ocr/general HTTP/1.1
Authorization: FCHXdPTEwMDAwMzc5Jms9QUtJRGVRZDBrRU1yM2J4ZjhRckJi==
Host: recognition.image.myqcloud.com
Content-Length: 735
Content-Type: multipart/form-data;boundary=--------------acebdf13572468

----------------acebdf13572468
Content-Disposition: form-data; name="appid";

123456
----------------acebdf13572468
Content-Disposition: form-data; name="bucket";

test
----------------acebdf13572468
Content-Disposition: form-data; name="image"; filename="test.jpg"
Content-Type: image/jpeg

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
----------------acebdf13572468--

說實話看到這種格式,一開始真是一臉懵逼,前面幾個參數還好,後面這一大串不知道怎麼傳遞過去,後來百度了一下,這種格式符合RFC 2045協議

具體解釋一下:

第5行:聲明Content Type類型,並定義邊界字符串。邊界符能夠自定義,不過最好是用破折號等數據中通常不會出現的字符;

第六、九、13以及18行是換行,傳遞的時候使用‘\r\n';

第七、11以及15行是‘--’加上第5行的boundary即邊界字符串。這裏要注意是必定要加上前面的連字符‘--’,我開始沒注意寫的是和boundary同樣,結果一直報錯。

第八、十、十二、14就是傳遞的Key_Value類型的數據。「appid」和「bucket」就是要傳遞的key,而「123456」以及「test」就是分別對應的value。

第1六、17行表明另一個數據,key是image,filename是「test.jpg」。

最後兩行就是結束了。注意最後一行是boundary加上‘--’。

弄清楚是什麼意思了,就能夠開始寫代碼了。這裏咱們用WebRequest,至於爲何不用HttpClient,研究ing。不知哪位仁兄使用HttpClient寫過,請不吝賜教。

四、具體實現

要識別的圖片以下:

HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(new Uri(postUrl));
Stream memStream = new MemoryStream();
webReq.Method = "POST";
string boundary = "--------------" + DateTime.Now.Ticks.ToString("x");// 邊界符  
webReq.ContentType = "multipart/form-data; boundary=" + boundary;

接下來是一個換行符,對應第6行:

byte[] enter = Encoding.ASCII.GetBytes("\r\n");  //換行
memStream.Write(enter, 0, enter.Length);                       

傳遞key_value的時候格式都是同樣,因而咱們寫在一個循環裏面:

Dictionary<string, string> dic = new Dictionary<string, string>()
{
    {"appid","1255710379"} ,
    {"bucket","test1"}
};
//寫入文本字段
string inputPartHeaderFormat = "--" + boundary + "\r\n" + "Content-Disposition:form-data;name=\"{0}\";" + "\r\n\r\n{1}\r\n";
foreach (var kv in dic)
{
    string inputPartHeader = string.Format(inputPartHeaderFormat, kv.Key, kv.Value);
    var inputPartHeaderBytes = Encoding.ASCII.GetBytes(inputPartHeader);
    memStream.Write(inputPartHeaderBytes, 0, inputPartHeaderBytes.Length);
}

接着該寫入image了,這裏咱們在bin/debug裏面有一張名爲1.jpg的圖片(即爲上面的圖片)。

var fileStream = new FileStream("1.jpg", FileMode.Open, FileAccess.Read);
// 寫入文件  
string imagePartHeader = "--" + boundary + "\r\n" +
                         "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
                         "Content-Type: image/jpeg\r\n\r\n";
var header = string.Format(imagePartHeader, "image", "1.jpg");
var headerbytes = Encoding.UTF8.GetBytes(header);
memStream.Write(headerbytes, 0, headerbytes.Length);
var buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
    memStream.Write(buffer, 0, bytesRead);
} 

最後就是結束符了:

 // 最後的結束符  
byte[] endBoundary = Encoding.ASCII.GetBytes("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "\r\n" + boundary + "--\r\n");
memStream.Write(endBoundary, 0, endBoundary.Length); 

接下來設置其餘Header參數:

webReq.ContentLength = memStream.Length;
webReq.Headers.Add(HttpRequestHeader.Authorization, aut);
webReq.Host = "recognition.image.myqcloud.com";

這裏須要注意的一點是設置Host值的時候不能使用

webReq.Headers.Add(HttpRequestHeader.Host, "recognition.image.myqcloud.com");

這個方法,不然會有異常。 

var requestStream = webReq.GetRequestStream();
memStream.Position = 0;
memStream.CopyTo(requestStream);
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
var ret = sr.ReadToEnd();
sr.Close();
response.Close();
requestStream.Close();
memStream.Close();

完美運行!識別結果以下:

 

4、須要注意的點

一、計算Authorization受權簽名

簽名分爲屢次有效簽名和單次有效簽名。它們拼接成的簽名串格式爲:

a=[appid]&b=[bucket]&k=[SecretID]&e=[expiredTime]&t=[currentTime]&r=[rand]&u=[userid]&f=[fileid]

具體每一個字段的含義請參見官方文檔:簽名和鑑權文檔

須要注意的有兩點:

一、使用 HMAC-SHA1 算法對請求進行加密;

二、簽名串須要使用 Base64 編碼(首先對orignal使用HMAC-SHA1算法進行簽名,而後將orignal附加到簽名結果的末尾,再進行Base64編碼,獲得最終的sign)。

 /// <summary>
 /// HMAC-SHA1加密算法
 /// </summary>
 /// <param name="secret_key">密鑰</param>
 /// <param name="orignalStr">源文</param>
 /// <returns></returns>
 public static string HmacSha1Sign(string secret_key, string orignalStr)
 {
     var hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(secret_key));
     var orignalBytes = Encoding.UTF8.GetBytes(orignalStr);
     var hashBytes = hmacsha1.ComputeHash(orignalBytes);
     List<byte> bytes = new List<byte>();
     bytes.AddRange(hashBytes);
     bytes.AddRange(orignalBytes);
     return Convert.ToBase64String(bytes.ToArray());
 }

二、一些其餘須要注意的  

一、文中使用的appid、bucket、secret_id、secret_key須要註冊萬象優圖後,才能獲得。至於如何獲得,文檔中說的很清楚,有詳細的步驟。

二、在設置Header參數時,Content_Length和Host能夠不用設置。

5、最後

但願你在調用騰訊雲-OCR通用印刷體識別Api的時候能夠少走些彎路,少踩一些坑。固然了這些可能算不上坑,多是我的一些基礎知識沒掌握。無論怎麼樣,若是你在使用OCR的時候,本文對你有一點幫助,那它就發揮了應有的做用。

本文的源代碼有興趣的能夠下載

相關文章
相關標籤/搜索