Public/media/Get-OCR.ps1

# Credits https://github.com/HumanEquivalentUnit/PowerShell-Misc/blob/master/Get-Win10OcrTextFromImage.ps1

function Get-OCR {
    <#
.SYNOPSIS
    Runs Windows 10 OCR on an image.
 
.DESCRIPTION
    Takes a path to an image file, with some text on it.
    Runs Windows 10 OCR against the image.
    Returns an [OcrResult], hopefully with a .Text property containing the text
 
.PARAMETER -Path
    Path to an image file
 
.EXAMPLE
    Get-OCR -Path 'c:\test.bmp'
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 0,
            HelpMessage = 'Path to an image file, to run OCR on')]
        [ValidateNotNullOrEmpty()]
        $Path
    )

    Begin {
        # Add the WinRT assembly, and load the appropriate WinRT types
        Add-Type -AssemblyName System.Runtime.WindowsRuntime

        $null = [Windows.Storage.StorageFile, Windows.Storage, ContentType = WindowsRuntime]
        $null = [Windows.Media.Ocr.OcrEngine, Windows.Foundation, ContentType = WindowsRuntime]
        $null = [Windows.Foundation.IAsyncOperation`1, Windows.Foundation, ContentType = WindowsRuntime]
        $null = [Windows.Graphics.Imaging.SoftwareBitmap, Windows.Foundation, ContentType = WindowsRuntime]
        $null = [Windows.Storage.Streams.RandomAccessStream, Windows.Storage.Streams, ContentType = WindowsRuntime]

        # [Windows.Media.Ocr.OcrEngine]::AvailableRecognizerLanguages
        $ocrEngine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages()

        # PowerShell doesn't have built-in support for Async operations,
        # but all the WinRT methods are Async.
        # This function wraps a way to call those methods, and wait for their results.
        $getAwaiterBaseMethod = [WindowsRuntimeSystemExtensions].GetMember('GetAwaiter').
        Where({
                $PSItem.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'
            }, 'First')[0]

        Function Await {
            param($AsyncTask, $ResultType)

            $getAwaiterBaseMethod.
            MakeGenericMethod($ResultType).
            Invoke($null, @($AsyncTask)).
            GetResult()
        }
    }

    Process {
        foreach ($p in $Path) {
            # From MSDN, the necessary steps to load an image are:
            # Call the OpenAsync method of the StorageFile object to get a random access stream containing the image data.
            # Call the static method BitmapDecoder.CreateAsync to get an instance of the BitmapDecoder class for the specified stream.
            # Call GetSoftwareBitmapAsync to get a SoftwareBitmap object containing the image.
            #
            # https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/imaging#save-a-softwarebitmap-to-a-file-with-bitmapencoder

            # .Net method needs a full path, or at least might not have the same relative path root as PowerShell
            $p = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p)

            $params = @{
                AsyncTask  = [Windows.Storage.StorageFile]::GetFileFromPathAsync($p)
                ResultType = [Windows.Storage.StorageFile]
            }
            $storageFile = Await @params


            $params = @{
                AsyncTask  = $storageFile.OpenAsync([Windows.Storage.FileAccessMode]::Read)
                ResultType = [Windows.Storage.Streams.IRandomAccessStream]
            }
            $fileStream = Await @params


            $params = @{
                AsyncTask  = [Windows.Graphics.Imaging.BitmapDecoder]::CreateAsync($fileStream)
                ResultType = [Windows.Graphics.Imaging.BitmapDecoder]
            }
            $bitmapDecoder = Await @params


            $params = @{
                AsyncTask  = $bitmapDecoder.GetSoftwareBitmapAsync()
                ResultType = [Windows.Graphics.Imaging.SoftwareBitmap]
            }
            $softwareBitmap = Await @params

            # Run the OCR
            Await $ocrEngine.RecognizeAsync($softwareBitmap) ([Windows.Media.Ocr.OcrResult])
        }
    }
}