网站栏目下拉菜单,如何开网店,贵州建设厅考试网站二建成绩,建网站报价表构建代码
构建这个PC桌面应用#xff0c;我们需要几个步骤#xff1a;
在得到第一次的显示结果后#xff0c;经过测试#xff0c;有很大可能会根据结果再对界面进行调整#xff0c;实际上也是一个局部的软件工程中的迭代开发。
界面设计
启动Visual Studio 2017, 创建…构建代码
构建这个PC桌面应用我们需要几个步骤
在得到第一次的显示结果后经过测试有很大可能会根据结果再对界面进行调整实际上也是一个局部的软件工程中的迭代开发。
界面设计
启动Visual Studio 2017, 创建一个基于C#语言的WPF(Windows Presentation Foundation)项目
WPF是一个非常成熟的技术在有界面展示和交互的情况下使用XAML设计/渲染引擎比WinForm程序要强101倍再加上有C#语言利器的帮助是写PC桌面前端应用的最佳组合。
给Project起个名字比如叫“CartoonTranslate”选择最新的.NET Framework (4.6以上)然后点击”OK”。我们先一起来设计一下界面
Input URL用于输入互联网上的一张漫画图片的URL
Engine指的是两个不同的算法引擎其中OCR旧引擎可以支持25种语言识别效果可以接受而Recognize Text新引擎目前只能支持英文但效果比较好。
Language制定当前要翻译的漫画的语言我们只以英文和日文为例其它国家的漫画相对较少但一通百通一样可以支持。
右侧的一堆Button了解一下
Show展示Input URL中的图片到下面的图片区
OCR调用OCR服务
Translate调用文本翻译服务将日文或者英文翻译成中文
下侧大面积的图片区了解一下
Source Image原始漫画图片
Target Image翻译成中文对白后的漫画图片
界面设计代码
我们在MainWindow.xaml文件里面填好以下code
Window x:ClassCartoonTranslate.MainWindowxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlxmlns:dhttp://schemas.microsoft.com/expression/blend/2008xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006xmlns:localclr-namespace:CartoonTranslatemc:IgnorabledTitleMainWindow Height450 Width800GridGrid.RowDefinitionsRowDefinition HeightAuto/RowDefinition HeightAuto/RowDefinition HeightAuto/RowDefinition Height*//Grid.RowDefinitionsStackPanel OrientationHorizontal Grid.Row0TextBlock Grid.Row0 TextInput URL:/TextBox x:Nametb_Url Grid.Row1 Width600Texthttp://stat.ameba.jp/user_images/20121222/18/secretcube/2e/19/j/o0800112012341269548.jpg/Button x:Namebtn_Show ContentShow Clickbtn_Show_Click Width100/Button x:Namebtn_OCR ContentOCR Clickbtn_OCR_Click Width100/Button x:Namebtn_Translate ContentTranslate Clickbtn_Translate_Click Width100//StackPanelStackPanel Grid.Row1 OrientationHorizontalTextBlock TextEngine:/RadioButton x:Namerb_V1 GroupNamegn_Engine ContentOCR Margin20,0 IsCheckedTrue Clickrb_V1_Click/RadioButton x:Namerb_V2 GroupNamegn_Engine ContentRecognize Text Clickrb_V2_Click/TextBlock TextLanguage: Margin20,0/RadioButton x:Namerb_English GroupNamegn_Language ContentEnglish/RadioButton x:Namerb_Japanese GroupNamegn_Language ContentJapanese IsCheckedTrue Margin20,0//StackPanelGrid Grid.Row3Grid.ColumnDefinitionsColumnDefinition Width*/ColumnDefinition Width40/ColumnDefinition Width*//Grid.ColumnDefinitionsTextBlock Grid.Column0 TextSource Image VerticalAlignmentCenter HorizontalAlignmentCenter/TextBlock Grid.Column2 TextTarget Image VerticalAlignmentCenter HorizontalAlignmentCenter/Image x:NameimgSource Grid.Column0 StretchNone HorizontalAlignmentLeft VerticalAlignmentTop/Image x:NameimgTarget Grid.Column2 StretchNone HorizontalAlignmentLeft VerticalAlignmentTop/Canvas x:Namecanvas_1 Grid.Column0/Canvas x:Namecanvas_2 Grid.Column2//Grid
/Grid
/Window 处理事件
关于XAML语法的问题不在本文的讨论范围之内。上面的XAML写好后编译时会出错因为里面定义了很多事件在C#文件中还没有实现。所以我们现在把事件代码补上。
局部变量定义在MainWindow.xaml.cs的MainWindow class里面
// using “OCR” or “Recognize Text”
private string Engine;
// source language, English or Japanese
private string Language;
// OCR result object
private OcrResult.Rootobject ocrResult; 按钮”Show”的事件
点击Show按钮的事件把URL中的漫画的地址所指向的图片加载到窗口中显示
private void btn_Show_Click(object sender, RoutedEventArgs e)
{if (!Uri.IsWellFormedUriString(this.tb_Url.Text, UriKind.Absolute)){// show warning messagereturn;}// show image at imgSourceBitmapImage bi new BitmapImage();bi.BeginInit();bi.UriSource new Uri(this.tb_Url.Text);bi.EndInit();this.imgSource.Source bi;this.imgTarget.Source bi;
} 在上面的代码中同时给左右两个图片区域赋值显示两张一样的图片。
按钮”OCR”的事件
点击OCR按钮的事件会调用OCR REST API然后根据返回结果把所有识别出来的文字用红色的矩形框标记上
private async void btn_OCR_Click(object sender, RoutedEventArgs e)
{this.Engine GetEngine();this.Language GetLanguage();if (Engine OCR){ocrResult await CognitiveServiceAgent.DoOCR(this.tb_Url.Text, Language);foreach (OcrResult.Region region in ocrResult.regions){foreach (OcrResult.Line line in region.lines){if (line.Convert()){Rectangle rect new Rectangle(){Margin new Thickness(line.BB[0], line.BB[1], 0, 0),Width line.BB[2],Height line.BB[3],Stroke Brushes.Red,//Fill Brushes.White};this.canvas_1.Children.Add(rect);}}}}else{}
} 在上面的代码中通过调用DoOCR()自定义函数返回了反序列化好的类再依次把返回结果集中的每个矩形生成一个Rectangle图形类它的left和top用Margin的方式来定义width和height直接赋值即可把这些Rectangle图形类的实例添加到canvas_1的Visual Tree里即可显示出来这个就是WPF的好处啦不用处理绘图事件但性能不如用Graphics类直接绘图。
按钮”Translate”的事件
点击Translate按钮的事件
private async void btn_Translate_Click(object sender, RoutedEventArgs e)
{Liststring listTarget await this.Translate();this.ShowTargetText(listTarget);
}
private async TaskListstring Translate()
{Liststring listSource new Liststring();Liststring listTarget new Liststring();if (this.Version OCR){foreach (OcrResult.Region region in ocrResult.regions){foreach (OcrResult.Line line in region.lines){listSource.Add(line.TEXT);if (listSource.Count 25){Liststring listOutput await CognitiveServiceAgent.DoTranslate(listSource, Language, zh-Hans);listTarget.AddRange(listOutput);listSource.Clear();}}}if (listSource.Count 0){Liststring listOutput await CognitiveServiceAgent.DoTranslate(listSource, Language, zh-Hans);listTarget.AddRange(listOutput);}}return listTarget;
}
private void ShowTargetText(Liststring listTarget)
{int i 0;foreach (OcrResult.Region region in ocrResult.regions){foreach (OcrResult.Line line in region.lines){string translatedLine listTarget[i];Rectangle rect new Rectangle(){Margin new Thickness(line.BB[0], line.BB[1], 0, 0),Width line.BB[2],Height line.BB[3],Stroke null,Fill Brushes.White};this.canvas_2.Children.Add(rect);TextBlock tb new TextBlock(){Margin new Thickness(line.BB[0], line.BB[1], 0, 0),Height line.BB[3],Width line.BB[2],Text translatedLine,FontSize 16,TextWrapping TextWrapping.Wrap,Foreground Brushes.Red};this.canvas_2.Children.Add(tb);i;}}
} 上面这段代码中包含了两个函数this.Translate()和this.ShowTargetText()。
我们先看第一个函数最难理解的地方可能是有个“25“数字这是因为Translate API允许一次提交多个字符串并一起返回结果这样比你提交25次字符串要快的多。翻译好的结果按顺序放在listOutput里供后面使用。
再看第二个函数先根据原始文字的矩形区域生成一些白色的实心矩形把它们贴在右侧的目标图片上达到把原始文字覆盖扣去的目的。然后再根据每个原始矩形生成一个TextBlock设定好它的位置和尺寸再设置好翻译后的结果(translatedLine)这样就可以把中文文字贴到图上了。
选项按钮的事件
点击Radio Button的事件
private void rb_V1_Click(object sender, RoutedEventArgs e)
{this.rb_Japanese.IsEnabled true;
}
private void rb_V2_Click(object sender, RoutedEventArgs e)
{this.rb_English.IsChecked true;this.rb_Japanese.IsChecked false;this.rb_Japanese.IsEnabled false;
}
private string GetLanguage()
{if (this.rb_English.IsChecked true){return en;}else{return ja;}
}
private string GetEngine()
{if (this.rb_V1.IsChecked true){return OCR;}else{return RecText;}
} API数据访问部分
我们需要在CatroonTranslate工程中添加以下三个.cs文件 CognitiveServiceAgent.cs OcrResult.cs TranslateResult.cs
与认知服务交互
CognitiveServiceAgent.cs文件完成与REST API交互的工作包括调用OCR服务的和调用翻译服务的代码
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace CartoonTranslate
{class CognitiveServiceAgent{const string OcrEndPointV1 https://westcentralus.api.cognitive.microsoft.com/vision/v2.0/ocr?detectOrientationtruelanguage;const string OcrEndPointV2 https://westcentralus.api.cognitive.microsoft.com/vision/v2.0/recognizeText?modePrinted;const string VisionKey1 4c20ac56e1e7459a05e1497270022b;const string VisionKey2 97992f0987e4be6b5be132309b8e57;const string UrlContentTemplate {{\url\:\{0}\}};
const string TranslateEndPoint https://api.cognitive.microsofttranslator.com/translate?api-version3.0from{0}to{1};const string TKey1 04023df3a4c499b1fc82510b48826c;const string TKey2 9f76381748549cb503dae4a0d80a80;
public static async TaskListstring DoTranslate(Liststring text, string fromLanguage, string toLanguage){try{using (HttpClient hc new HttpClient()){hc.DefaultRequestHeaders.Add(Ocp-Apim-Subscription-Key, TKey1);string jsonBody CreateJsonBodyElement(text);StringContent content new StringContent(jsonBody, Encoding.UTF8, application/json);string uri string.Format(TranslateEndPoint, fromLanguage, toLanguage);HttpResponseMessage resp await hc.PostAsync(uri, content);string json await resp.Content.ReadAsStringAsync();var ro Newtonsoft.Json.JsonConvert.DeserializeObjectListTranslateResult.Class1(json);Liststring list new Liststring();foreach(TranslateResult.Class1 c in ro){list.Add(c.translations[0].text);}return list;}}catch (Exception ex){Debug.WriteLine(ex.Message);return null;}}
private static string CreateJsonBodyElement(Liststring text){var a text.Select(t new { Text t }).ToList();var b JsonConvert.SerializeObject(a);return b;}
/// summary/// /// /summary/// param nameimageUrl/param/// param namelanguageen, ja, zh/param/// returns/returnspublic static async TaskOcrResult.Rootobject DoOCR(string imageUrl, string language){try{using (HttpClient hc new HttpClient()){ByteArrayContent content CreateHeader(hc, imageUrl);var uri OcrEndPointV1 language;HttpResponseMessage resp await hc.PostAsync(uri, content);string result string.Empty;if (resp.StatusCode System.Net.HttpStatusCode.OK){string json await resp.Content.ReadAsStringAsync();Debug.WriteLine(json);OcrResult.Rootobject ro Newtonsoft.Json.JsonConvert.DeserializeObjectOcrResult.Rootobject(json);return ro;}}return null;}catch (Exception ex){Debug.Write(ex.Message);return null;}}
private static ByteArrayContent CreateHeader(HttpClient hc, string imageUrl){hc.DefaultRequestHeaders.Add(Ocp-Apim-Subscription-Key, VisionKey1);string body string.Format(UrlContentTemplate, imageUrl);byte[] byteData Encoding.UTF8.GetBytes(body);var content new ByteArrayContent(byteData);content.Headers.ContentType new MediaTypeHeaderValue(application/json);return content;}}
} 其中DoTranslate()函数和DoOCR()函数都是HTTP调用很容易理解。只有CreateJsonBodyElement函数需要解释一下。前面提到过我们一次允许给服务器提交25个字符串做批量翻译因此传进来的是个Liststring经过这个函数的简单处理会得到以下JSON格式的数据作为HTTP的Body
// JSON Data as Body
[{“Text” : ”第1个字符串”},{“Text” : ”第2个字符串”},……..{“Text” : ”第25个字符串”},
] OCR服务的数据类定义
OcrResult.cs文件是OCR服务返回的JSON数据所对应的类用于反序列化
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CartoonTranslate.OcrResult
{public class Rootobject{public string language { get; set; }public string orientation { get; set; }public float textAngle { get; set; }public Region[] regions { get; set; }}
public class Region{public string boundingBox { get; set; }public Line[] lines { get; set; }}
public class Line{public string boundingBox { get; set; }public Word[] words { get; set; }
public int[] BB { get; set; }public string TEXT { get; set; }
public bool Convert(){CombineWordToSentence();return ConvertBBFromString2Int();}
private bool ConvertBBFromString2Int(){string[] tmp boundingBox.Split(new char[] { , }, StringSplitOptions.RemoveEmptyEntries);if (tmp.Length 4){BB new int[4];for (int i 0; i 4; i){int.TryParse(tmp[i], out BB[i]);}return true;}return false;}
private void CombineWordToSentence(){StringBuilder sb new StringBuilder();foreach (Word word in words){sb.Append(word.text);}this.TEXT sb.ToString();}
}
public class Word{public string boundingBox { get; set; }public string text { get; set; }}
} 需要说明的是服务器返回的boundingBox是个string类型在后面使用起来不太方便需要把它转换成整数所以增加了CovertBBFromString2Int()函数。还有就是返回的是一个个的词(Word)而不是一句话所以增加了CombineWordToSentence()来把词连成句子。
翻译服务的数据类定义
TranslateResult.cs文件翻译服务返回的JSON所对应的类用于反序列化
namespace CartoonTranslate.TranslateResult
{public class Class1{public Translation[] translations { get; set; }}
public class Translation{public string text { get; set; }public string to { get; set; }}
} 小提示在VS2017中这种类不需要手工键入可以在Debug模式下先把返回的JSON拷贝下来然后新建一个.cs文件在里面用Paste Special从JSON直接生成类就可以了。