Skip to main content

让AI帮助人们更好的阅读 - 使用Azure认知服务之沉浸式阅读器

分类:  Azure认知服务 标签:  #Azure #人工智能 #沉浸式阅读器 发布于: 2023-06-06 21:30:58

什么是沉浸式阅读器?
我们可以快速的看一个图,该如下:


这个就是微软的浏览器Edge自带的沉浸式阅读器,使用微软的edge浏览器打开一个网页,在地址栏上选择沉浸式阅读器的按钮,即可以启动沉浸式阅读器。从该图片我们可以看到,可以使用沉浸式阅读器大大改善阅读的环境,用户可以使用该工具进行字体的调整,需要聚焦的行数,甚至可以使用语音将全篇文章自动阅读完成,可以极大得帮助到一些有阅读障碍得人士。应用的场景也是很多的,例如专业的文献,例如需要即时翻译,例如针对于有些阅读障碍的人士等等。

微软通过在Azure上提供沉浸式阅读服务将这个功能开放给普通用户,普通用户可以通过SDK或者API集成该工具,将沉浸式阅读的功能集成到自己的产品中,不仅仅是支持桌面产品,还支持安卓和iOS。

下面我们来快速的给大家讲解一下具体的步骤,这个工具使用起来还是很简单的。

创建Azure 沉浸式阅读服务(Immersive Service)和配置AAD权限

在开始之前我们需要首先给大家普及一个概念: Azure Activity Directory的权限和应用。如果大家熟悉什么是QAuth2.0或者OpenID Connection,或者对于标识管理等概念有知道,那么理解Azure AAD还是比较简单的。如果我们需要使用沉浸式服务,我们是需要使用JavaScript远程对您创建的Immersive Service服务进行调用,这就要求你需要一个授权的给到您的应用访问您在Azure上的资源,对于Azure来说大多数可以使用Client认证的方式来使用服务。Azure AAD可以让用户注册一个Client Application, 然后使用该client的信息,在资源里对该client进行授权,然后在客户应用里集成SDK的时候,使用该client的信息访问Azure的资源服务。如果您不熟悉QAuth2或者Azure AAD请找一下相应的文档学习一下。

创建Azure Immersive Service(沉浸式阅读服务)

通过地址Azure Global或者Azure China访问Azure Portal, 使用左上角的菜单搜索Immersive, 找到沉浸式阅读服务,如下图:


找到该服务之后,点击Create按钮进行创建, 创建界面非常简单,只需要选择您的订阅,选择你需要的区域,给出名字,然后选择定价层,最后点击Review create就可以了。如下图:


点击之后就可以等待创建成功了。

创建Client以及给Immersive Service添加新授权

我们前面介绍过了为什么要通过client的授权来访问Immersive Server的服务,那么接下来我们向大家展示一下如果做。

创建新的Client

登录到Azure Portal, 使用左上侧的菜单,选择Azure Activity Directory, 然后选择App registrations, 如下图:


然后选择New registrations, 给您自己的app一个名字,其他默认就可以了。如下图:


点击创建之后,创建成功后,进入到该App的overview页面,我们需要记住该页面上的几个信息:

  • Application (Client) ID
  • Directory (tenant) ID

另外我们还需要给该应用创建一个secrets, 选择左侧的菜单Certificates & secrets, 如下图:


点击进去之后,再点击New client secret, 选择该secrets的有效时间,和起一个名字,就可以了。如下图:


注意
创建secrets之后,只有在第一创建的时候会显示出来,刷新页面之后就再也找不到了。所以必须在第一次创建成功后,立即将该secret复制并保存起来,否则的话,就只能再重新创建一次了。

给Client授权访问Immersive Service

登录到Azure Portal之后找到您的immersive service的资源,进入该资源的overview页面,从左侧菜单找到Access control(IAM),如下图:


然后选择Add role assignment, 如下图:


然后注意在Role里选择Cognitive Service User角色,在Select框里输入您的client名字,授权即可。


至此我们应该收集到了Client和服务相关的如下信息:

  • Client ID
  • Tenant Id
  • Client Secret
  • Immersive Service Name

我们接下来使用这些信息来创建一个简单的应用,演示如何使用沉浸式阅读服务。

创建自己的应用并集成沉浸式阅读服务

您可以使用.net core的命令行工具加上+ VS code来做这件事,您可以直接使用Visual Studio 来完成这件事,为了方便,我们使用Visual Studio来完成这个小项目。

启动Visual studio,创建一个新的基于ASP.net Core MVC的简单应用。创建完成后,在项目浏览器上点击右键,选择Manage User Secrets, IDE会为项目添加一个secrets.json的文件,对应的需要上述我们已经得到的四个数据,需要注意的是Subdomain指的是我们的Immersive Service的名字。


secrets.json的主要内容如下:

{
  "TenantId": "YOUR_TENANT_ID",
  "ClientId": "YOUR_CLIENT_ID",
  "ClientSecret": "YOUR_CLIENT_SECRET",
  "Subdomain": "YOUR_SUBDOMAIN"
}

安装需要的nuget

使用nuget的管理工具添加必要的包:

Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory

从代码中进行集成

跟着我们需要从代码中将沉浸式阅读服务集成到我们自己的应用中来。在集成之前需要理解的一点是,沉浸式阅读服务实际上是通过iframe进行调用,因此你需要在需要该工具的页面上进行集成,当然如果你要集成到整个站点的所有页面上,那么你需要将公共的代码放到模板里或者是其他的机制保证这部分代码都能被所有的页面访问得到。

在Controller里添加必要的代码

我们需要在Controller里添加代码引入我们的配置值,例如Client ID等等,同时我们需要通过AAD的库拿到Client的认证数据,用于页面上。这个部分代码是很容易看懂的,代码如下:

private readonly string TenantId;     // Azure subscription TenantId
private readonly string ClientId;     // Azure AD ApplicationId
private readonly string ClientSecret; // Azure AD Application Service Principal password
private readonly string Subdomain;    // Immersive Reader resource subdomain (resource 'Name' if the resource was created in the Azure portal, or 'CustomSubDomain' option if the resource was created with Azure CLI Powershell. Check the Azure portal for the subdomain on the Endpoint in the resource Overview page, for example, 'https://[SUBDOMAIN].cognitiveservices.azure.com/')

public HomeController(Microsoft.Extensions.Configuration.IConfiguration configuration)
{
    TenantId = configuration["TenantId"];
    ClientId = configuration["ClientId"];
    ClientSecret = configuration["ClientSecret"];
    Subdomain = configuration["Subdomain"];

    if (string.IsNullOrWhiteSpace(TenantId))
    {
        throw new ArgumentNullException("TenantId is null! Did you add that info to secrets.json?");
    }

    if (string.IsNullOrWhiteSpace(ClientId))
    {
        throw new ArgumentNullException("ClientId is null! Did you add that info to secrets.json?");
    }

    if (string.IsNullOrWhiteSpace(ClientSecret))
    {
        throw new ArgumentNullException("ClientSecret is null! Did you add that info to secrets.json?");
    }

    if (string.IsNullOrWhiteSpace(Subdomain))
    {
        throw new ArgumentNullException("Subdomain is null! Did you add that info to secrets.json?");
    }
}

/// <summary>
/// Get an Azure AD authentication token
/// </summary>
private async Task<string> GetTokenAsync()
{
    string authority = $"https://login.windows.net/{TenantId}";
    const string resource = "https://cognitiveservices.azure.com/";

    AuthenticationContext authContext = new AuthenticationContext(authority);
    ClientCredential clientCredential = new ClientCredential(ClientId, ClientSecret);

    AuthenticationResult authResult = await authContext.AcquireTokenAsync(resource, clientCredential);

    return authResult.AccessToken;
}

[HttpGet]
public async Task<JsonResult> GetTokenAndSubdomain()
{
    try
    {
        string tokenResult = await GetTokenAsync();

        return new JsonResult(new { token = tokenResult, subdomain = Subdomain });
    }
    catch (Exception e)
    {
        string message = "Unable to acquire Azure AD token. Check the debugger for more information.";
        Debug.WriteLine(message, e);
        return new JsonResult(new { error = message });
    }
}

在模板中添加共享的CSS

从项目里打开模板共享文件:Views\Shared\Layout.cshtml

@RenderSection("Styles", required: false)

更改view: Views\Home\Index.cshtml

用以下的内容进行替换

@{
    ViewData["Title"] = "Immersive Reader C# Quickstart";
}

@section Styles {
    <style type="text/css">
        .immersive-reader-button {
            background-color: white;
            margin-top: 5px;
            border: 1px solid black;
            float: right;
        }
    </style>
}

<div class="container">
    <button class="immersive-reader-button" data-button-style="iconAndText" data-locale="en"></button>

    <h1 id="ir-title">About Immersive Reader</h1>
    <div id="ir-content" lang="en-us">
        <p>
            Immersive Reader is a tool that implements proven techniques to improve reading comprehension for emerging readers, language learners, and people with learning differences.
            The Immersive Reader is designed to make reading more accessible for everyone. The Immersive Reader
            <ul>
                <li>
                    Shows content in a minimal reading view
                </li>
                <li>
                    Displays pictures of commonly used words
                </li>
                <li>
                    Highlights nouns, verbs, adjectives, and adverbs
                </li>
                <li>
                    Reads your content out loud to you
                </li>
                <li>
                    Translates your content into another language
                </li>
                <li>
                    Breaks down words into syllables
                </li>
            </ul>
        </p>
        <h3>
            The Immersive Reader is available in many languages.
        </h3>
        <p lang="es-es">
            El Lector inmersivo está disponible en varios idiomas.
        </p>
        <p lang="zh-cn">
            沉浸式阅读器支持许多语言
        </p>
        <p lang="de-de">
            Der plastische Reader ist in vielen Sprachen verfügbar.
        </p>
        <p lang="ar-eg" dir="rtl" style="text-align:right">
            يتوفر \"القارئ الشامل\" في العديد من اللغات.
        </p>
    </div>
</div>

在视图里添加启动Immersive Service Reader的代码

我们还是在视图: Views\Home\Index.cshtml的底部加上如下的代码:

@section Scripts
{
    <script src="https://contentstorage.onenote.office.net/onenoteltir/immersivereadersdk/immersive-reader-sdk.1.0.0.js"></script>
    <script>
        function getTokenAndSubdomainAsync() {
            return new Promise(function (resolve, reject) {
                $.ajax({
                    url: "@Url.Action("GetTokenAndSubdomain", "Home")",
                    type: "GET",
                    success: function (data) {
                        if (data.error) {
                            reject(data.error);
                        } else {
                            resolve(data);
                        }
                    },
                    error: function (err) {
                        reject(err);
                    }
                });
            });
        }

        $(".immersive-reader-button").click(function () {
            handleLaunchImmersiveReader();
        });

        function handleLaunchImmersiveReader() {
            getTokenAndSubdomainAsync()
                .then(function (response) {
                    const token = response["token"];
                    const subdomain = response["subdomain"];

                    // Learn more about chunk usage and supported MIME types https://docs.microsoft.com/en-us/azure/cognitive-services/immersive-reader/reference#chunk
                    const data = {
                        title: $("#ir-title").text(),
                        chunks: [{
                            content: $("#ir-content").html(),
                            mimeType: "text/html"
                        }]
                    };

                    // Learn more about options https://docs.microsoft.com/en-us/azure/cognitive-services/immersive-reader/reference#options
                    const options = {
                        "onExit": exitCallback,
                        "uiZIndex": 2000,
                        "uiLang": "zh-CN"
                    };

                    ImmersiveReader.launchAsync(token, subdomain, data, options)
                        .catch(function (error) {
                            alert("Error in launching the Immersive Reader. Check the console.");
                            console.log(error);
                        });
                })
                .catch(function (error) {
                    alert("Error in getting the Immersive Reader token and subdomain. Check the console.");
                    console.log(error);
                });
        }

        function exitCallback() {
            console.log("This is the callback function. It is executed when the Immersive Reader closes.");
        }
    </script>

至此我们的项目就完成了,启动该项目,展示以下效果:

初次启动:


点击启动沉浸式阅读之后


调整自动翻译


更改外观