最近做项目需要用到识别图片中文字的功能,本来用的 Tesseract 这个写的,不过效果不是很理想。
随后上网搜了一下 OCR 接口,就准备使用腾讯云、百度的 OCR 接口试一下效果。不过这个腾讯云 OCR 就折腾了一天!
首先附上文档地址: OCR - 通用印刷体识别
有两种调用方式: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"
- }
看到这种格式,心里大概有数了,开始敲代码:
- 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(url, 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 的几率毕竟少,项目中基本上都是上传一张本地图片,然后识别出来文字。
- 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 类型,并定义边界字符串。边界符可以自定义,不过最好是用破折号等数据中一般不会出现的字符;
第 6、9、13 以及 18 行是换行,传递的时候使用'\r\n';
第 7、11 以及 15 行是'--'加上第 5 行的 boundary 即边界字符串。这里要注意是一定要加上前面的连字符'--',我开始没注意写的是和 boundary 一样,结果一直报错。
第 8、10、12、14 就是传递的 Key_Value 类型的数据。"appid" 和 "bucket" 就是要传递的 key,而 "123456" 以及 "test" 就是分别对应的 value。
第 16、17 行代表另外一个数据,key 是 image,filename 是 "test.jpg"。
最后两行就是结束了。注意最后一行是 boundary 加上'--'。
弄清楚是什么意思了,就可以开始写代码了。这里我们用 webRequest,至于为什么不用 HttpClient,研究 ing。不知哪位仁兄使用 HttpClient 写过,请不吝赐教。
假设要识别的图片如下(从百度上找了一张):
- HttpWebRequest webReq = (HttpWebRequest) WebRequest.Create(new Uri(url));
- 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();
完美运行!识别结果如下:
签名分为多次有效签名和单次有效签名。它们拼接成的签名串格式为:
- a = [appid] & b = [bucket] & k = [SecretID] & e = [expiredTime] & t = [currentTime] & r = [rand] & u = [userid] & f = [fileid]
具体每个字段的含义请参见官方文档: 签名和鉴权文档
需要注意的有两点:
1、使用 HMAC-SHA1 算法对请求进行加密;
2、签名串需要使用 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需要注册 万象优图 后,才能得到。至于如何得到,文档中说的很清楚,有详细的步骤。
希望你在调用腾讯云 - OCR 通用印刷体识别 Api 的时候可以少走些弯路,少踩一些坑。当然了这些可能算不上坑,可能是个人一些基础知识没掌握。不管怎么样,如果你在使用 OCR 的时候,本文对你有一点帮助,那它就发挥了应有的作用。
本文的源代码有兴趣的可以 下载 。
来源: http://www.cnblogs.com/liuyoung/p/8082563.html