本教程分为两个部分:第一部分是构建一个处理图像标注和翻译成不同语言的WebAPI服务;第二部分是从Android应用中调用这个RESTful服务。
将从创建一个新的WebAPI项目开始。在Visual Studio中选择新建项目 -> C# -> Web -> ASP.NET Web应用程序 - 空。勾选WebAPI,并选择云托管,以便稍后能够发布这个项目。
项目所需的所有库将包含在packages.config
文件中。
XML配置示例:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45"/>
<package id="Google.Apis" version="1.19.0" targetFramework="net45"/>
<package id="Google.Apis.Auth" version="1.19.0" targetFramework="net45"/>
<package id="Google.Apis.Core" version="1.19.0" targetFramework="net45"/>
<package id="Google.Apis.Translate.v2" version="1.19.0.543" targetFramework="net45"/>
<package id="Google.Apis.Vision.v1" version="1.19.0.683" targetFramework="net45"/>
<package id="GoogleApi" version="2.0.13" targetFramework="net45"/>
<package id="log4net" version="2.0.3" targetFramework="net45"/>
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net45"/>
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45"/>
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45"/>
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45"/>
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.0" targetFramework="net45"/>
<package id="Microsoft.Net.Compilers" version="1.0.0" targetFramework="net45" developmentDependency="true"/>
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45"/>
<package id="Zlib.Portable.Signed" version="1.11.0" targetFramework="net45"/>
</packages>
由于将使用Google API,因此需要先设置一个Google云视觉API项目。
1. 对于Google视觉API,下载VisionAPI-xxxxxx.json文件并保存在项目根目录。
2. 对于翻译API,在同一页面获取API密钥。
在代码中,首先调用这些API变量。将值替换为上面获取的密钥。
在Global
类中配置全局变量和环境变量。
using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Web.Http;
namespace ThingTranslatorAPI2 {
public class Global : System.Web.HttpApplication {
public static String apiKey;
protected void Application_Start() {
GlobalConfiguration.Configure(WebApiConfig.Register);
apiKey = "API-KEY";
createEnvVar();
}
private static void createEnvVar() {
var GAC = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS");
if (GAC == null) {
var VisionApiKey = ConfigurationManager.AppSettings["VisionApiKey"];
if (VisionApiKey != null) {
var path = System.Web.Hosting.HostingEnvironment.MapPath("~/") + "YOUR-API-KEY.json";
Trace.TraceError("path: " + path);
File.WriteAllText(path, VisionApiKey);
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", path);
}
}
}
}
}
在WebApiConfig
中配置路由,使用属性路由而不是默认路由配置。
using System.Web.Http;
namespace ThingTranslatorAPI2 {
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MapHttpAttributeRoutes();
}
}
}
需要一个API控制器来处理请求并处理它们。请求应包含图像文件和想要翻译成的目标语言的语言代码。图像将在内存中处理,因此无需将其保存在磁盘上。
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Results;
using GoogleApi;
using GoogleApi.Entities.Translate.Translate.Request;
using TranslationsResource = Google.Apis.Translate.v2.Data.TranslationsResource;
namespace ThingTranslatorAPI2.Controllers {
[RoutePrefix("api")]
public class TranslatorController : ApiController {
[Route("upload")]
[HttpPost]
public async Task> Upload() {
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
String langCode = string.Empty;
var response = new Response();
byte[] buffer = null;
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var content in provider.Contents) {
if (content.Headers.ContentType != null && content.Headers.ContentType.MediaType.Contains("image"))
buffer = await content.ReadAsByteArrayAsync();
else
langCode = await content.ReadAsStringAsync();
}
var labels = LabelDetector.GetLabels(buffer);
try {
var bestMatch = labels[0].LabelAnnotations.FirstOrDefault()?.Description;
String translateText;
if (langCode == "en")
translateText = bestMatch;
else
translateText = TranslateText(bestMatch, "en", langCode);
response.Original = bestMatch;
response.Translation = translateText;
} catch (Exception ex) {
response.Error = ex.Message;
return Json(response);
}
return Json(response);
}
private String TranslateText(String text, String source, String target) {
var _request = new TranslateRequest {
Source = source,
Target = target,
Qs = new[] { text },
Key = Global.apiKey
};
try {
var _result = GoogleTranslate.Translate.Query(_request);
return _result.Data.Translations.First().TranslatedText;
} catch (Exception ex) {
return ex.Message;
}
}
}
}
对于图像标注,需要这个类LabelDetector.cs
。
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Vision.v1;
using Google.Apis.Vision.v1.Data;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ThingTranslatorAPI2 {
public class LabelDetector {
public static IList GetLabels(byte[] imageArray) {
try {
VisionService vision = CreateAuthorizedClient();
string imageContent = Convert.ToBase64String(imageArray);
var responses = vision.Images.Annotate(new BatchAnnotateImagesRequest() {
Requests = new[] {
new AnnotateImageRequest() {
Features = new[] {
new Feature() { Type = "LABEL_DETECTION" }
},
Image = new Image() { Content = imageContent }
}
}
}).Execute();
return responses.Responses;
} catch (Exception ex) {
Trace.TraceError(ex.StackTrace);
}
return null;
}
public static VisionService CreateAuthorizedClient() {
try {
GoogleCredential credential = GoogleCredential.GetApplicationDefaultAsync().Result;
if (credential.IsCreateScopedRequired) {
credential = credential.CreateScoped(new[] {
VisionService.Scope.CloudPlatform
});
}
return new VisionService(new BaseClientService.Initializer {
HttpClientInitializer = credential,
GZipEnabled = false
});
} catch (Exception ex) {
Trace.TraceError("CreateAuthorizedClient: " + ex.StackTrace);
}
return null;
}
}
}
Response.cs
将如下所示:
namespace ThingTranslatorAPI2.Controllers {
public class Response {
public string Original { get; set; }
public string Translation { get; set; }
public string Error { get; set; }
}
}
现在可以将这个项目发布到Azure云。转到“构建”->“发布”,并填写所有4个输入框以匹配Azure设置。
使用Postman测试响应:
{"Original":"mouse","Translation":"miš","Error":null}
收到了包含图像标签的英文和指定语言的翻译版本的响应。
目前有几种API可以处理图像标注。其中一个发现非常奇特的叫做CloudSight。尽管它比其他的更准确,但它依赖于人类标记。这的缺点是它比机器完成工作需要更多的时间。通常在10-30秒后收到响应。
可以想象如果应用程序运行并且发生了超时。该如何称呼它为连接超时还是咖啡休息超时;-)?