C++/WinRTでUWPその9　OCRとテキスト保存

[前回](https://qiita.com/lilac0011/items/b23180b72f25392122dc)の続きです。

・それではOCR機能を実装する前にサンプルを見てみます。[ここ](https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/OCR/cppwinrt)ですね。
・詳しくはサンプルを確認して欲しいですが、必要な部分（OcrFileImage.cppのExtractButton_Click関数　166～188行目）をみてみると。
・OcrEngineを使って、Languageを指定して、RecognizeAsyncにビットマップを渡せばText()で結果が受け取れると。
・それでは実装してみましょう。


#1. OCR機能の実装
##1-1. xamlの編集
・前回から見た目を少し変更してこんな感じにしました。

```xml:MainPage.xaml（変更後）
<Page
    x:Class="ocr_1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ocr_1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid RequestedTheme="Default">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="1*"/>
            <RowDefinition Height="5*" />
        </Grid.RowDefinitions>
        <Button x:Name="button1" Click="button1_Click" Content="クリックして画像を選べ" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FontSize="24" Margin="5,5,5,5"/>
        <Image x:Name="pic1" Grid.Row="1" Grid.RowSpan="2" Grid.Column="0"/>
        <TextBox x:Name="res1" Grid.Row="2" Grid.Column="1" Background="LightGray"/>
        <Button x:Name="button_ocr" Content="OCR" Click="button_ocr_Click" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FontSize="24" Margin="5,5,5,5"/>
        <Button x:Name="button_save" Content="テキスト保存" Click="button_save_Click" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FontSize="24" Margin="5,5,5,5"/>

    </Grid>
</Page>
```

![2-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/cc35e279-7e1e-496f-70a5-8d965641b5f7.png)

##1-2. MainPage.cppの編集
・サンプルで見たとおりに実装しました。MainPage.hでもbutton_ocr_Clickの型は変更してください。

```C++:MainPage.cpp（変更後）
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"


#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Storage.Pickers.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>
#include "winrt/Windows.Media.Ocr.h"


using namespace winrt;
using namespace Windows::UI::Xaml;


namespace winrt::ocr_1::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }
}

winrt::Windows::Foundation::IAsyncAction winrt::ocr_1::implementation::MainPage::button1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    winrt::Windows::Storage::Pickers::FileOpenPicker picPicker;
    picPicker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::Desktop);
    picPicker.FileTypeFilter().ReplaceAll({ L".jpg", L".jpeg", L".png", L".bmp" });

    auto picfile = co_await picPicker.PickSingleFileAsync();

    if (picfile == nullptr)
        res1().Text(L"選んで無いよ！");
    else
    {
        res1().Text(picfile.Path());

        auto picstream = co_await picfile.OpenAsync(winrt::Windows::Storage::FileAccessMode::Read);
        auto picdecoder = co_await winrt::Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(picstream);

        showpic = co_await picdecoder.GetSoftwareBitmapAsync(winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Bgra8, winrt::Windows::Graphics::Imaging::BitmapAlphaMode::Premultiplied);

        auto picsource = winrt::Windows::UI::Xaml::Media::Imaging::WriteableBitmap(showpic.PixelWidth(), showpic.PixelHeight());

        showpic.CopyToBuffer(picsource.PixelBuffer());
        pic1().Source(picsource);
    }
}


winrt::Windows::Foundation::IAsyncAction winrt::ocr_1::implementation::MainPage::button_ocr_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    winrt::Windows::Media::Ocr::OcrEngine ocr = nullptr;
    ocr = winrt::Windows::Media::Ocr::OcrEngine::TryCreateFromUserProfileLanguages();

    auto result = co_await ocr.RecognizeAsync(showpic);
    res1().Text(result.Text());
}


void winrt::ocr_1::implementation::MainPage::button_save_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{

}

```

・4行で終了です。前に[tesseract-ocr](https://qiita.com/lilac0011/items/9fe6e061c3a036689a36)でもOCRを実装しましたが、それより楽ちんですね。
・tesseractではutf8で返すのでそれをutf16へ変更する部分が必要ですし、なにより色々な下準備が大変でした。
・下のような画像を作ってそれを変換させてみたのがさらに下の画像です。

![text.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/441467fb-de09-7e69-ad53-5f830b2f257a.png)


![2-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131051/6edd06c4-92c3-d193-902c-334f1710d915.png)


・画像データの表示も、OCR機能の実装もC++/WinRTから使うんだったこんなに簡単に使えます。
・さて、変換後のテキストも保存できるようにしてみましょう。


#2. ファイルピッカーによるテキストの保存
・保存するときはFileSavePickerを使用します。画像を開くときに使用したFileOpenPickerと同じように使用可能です。
・テキストファイルの書き込みはWriteTextAsync()があるので、それを使用します。それではやってみましょう。

```C++:MainPage.cpp（変更後）
#include "pch.h"
#include "MainPage.h"
#include "MainPage.g.cpp"


#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Storage.Pickers.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>
#include "winrt/Windows.Media.Ocr.h"


using namespace winrt;
using namespace Windows::UI::Xaml;


namespace winrt::ocr_1::implementation
{
    MainPage::MainPage()
    {
        InitializeComponent();
    }
}

winrt::Windows::Foundation::IAsyncAction winrt::ocr_1::implementation::MainPage::button1_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    //ここで画像を選択して、表示します。

    winrt::Windows::Storage::Pickers::FileOpenPicker picPicker;
    picPicker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::Desktop);
    picPicker.FileTypeFilter().ReplaceAll({ L".jpg", L".jpeg", L".png", L".bmp" });

    auto picfile = co_await picPicker.PickSingleFileAsync();

    if (picfile == nullptr)
        res1().Text(L"選んで無いよ！");
    else
    {
        res1().Text(picfile.Path());

        auto picstream = co_await picfile.OpenAsync(winrt::Windows::Storage::FileAccessMode::Read);
        auto picdecoder = co_await winrt::Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(picstream);

        showpic = co_await picdecoder.GetSoftwareBitmapAsync(winrt::Windows::Graphics::Imaging::BitmapPixelFormat::Bgra8, winrt::Windows::Graphics::Imaging::BitmapAlphaMode::Premultiplied);

        auto picsource = winrt::Windows::UI::Xaml::Media::Imaging::WriteableBitmap(showpic.PixelWidth(), showpic.PixelHeight());

        showpic.CopyToBuffer(picsource.PixelBuffer());
        pic1().Source(picsource);
    }
}


winrt::Windows::Foundation::IAsyncAction winrt::ocr_1::implementation::MainPage::button_ocr_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    //ここで画像から文字を抽出します

    winrt::Windows::Media::Ocr::OcrEngine ocr = nullptr;
    ocr = winrt::Windows::Media::Ocr::OcrEngine::TryCreateFromUserProfileLanguages();

    auto result = co_await ocr.RecognizeAsync(showpic);
    res1().Text(result.Text());
}


winrt::Windows::Foundation::IAsyncAction winrt::ocr_1::implementation::MainPage::button_save_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e)
{
    //ここで抽出した文字列をテキストファイルへ保存します

    winrt::Windows::Storage::Pickers::FileSavePicker textPicker;
    textPicker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::Desktop);
    textPicker.FileTypeChoices().Insert(L"Plain Text", single_threaded_vector<hstring>({ L".txt" }));
    textPicker.SuggestedFileName(L"NewText");

    auto textfile = co_await textPicker.PickSaveFileAsync();
    if (textfile != nullptr)
    {
        co_await winrt::Windows::Storage::FileIO::WriteTextAsync(textfile, res1().Text());
    }
}
```

・以上で終了です。C++/WinRTからだとWinRT APIを使用するのは簡単でしょう？

[github](https://github.com/yoshiyoshi-git/ocr_1)も置いておきます。

・次は何をしようかなぁ・・・

[もくじへ](https://qiita.com/lilac0011/items/f53f7401ea7da31e3a0b)