다음을 통해 공유


AI 비디오 슈퍼 해상도(VSR) 시작하기

VSR(비디오 슈퍼 해상도)은 사람들의 저해상도 비디오 스트림을 지능적으로 확장하여 대역폭 제한, 열악한 네트워크 조건, 압축 또는 낮은 품질의 원본 콘텐츠로 인해 손실될 선명도와 세부 정보를 복원하는 AI 기반 비디오 업 샘플링 기술입니다.

앱에 VSR 기능을 추가하면 다음을 포함한 시나리오가 가능합니다.

  • 네트워크 연결 불량을 통해 비디오 품질 향상
  • CDN 비용을 줄이기 위한 대역폭 최적화
  • 여러 참가자가 있는 그룹 영상 통화와 같은 대역폭 높은 시나리오
  • 편집, 업로드 또는 보기에서 소셜 미디어 비디오 품질 향상

VSR 기능에는 현재 NPU가 있는 Copilot+ PC 가 필요합니다. 자세한 내용은 Copilot+ PC용 AI 애플리케이션 개발을 참조하세요.

이러한 VSR API는 ML(Machine Learning) 모델을 사용하며, 화상 통화 및 회의 앱, 사람의 얼굴을 말하는 소셜 및 짧은 형식 비디오와 같은 시나리오를 위해 특별히 설계되었습니다.

VSR은 현재 다음 해상도, 형식 및 FPS 범위를 지원합니다.

특성 지원되는 콘텐츠
입력 해상도 240p – 1440p
출력 해상도 480p – 1440p
FPS(초당 프레임 수) 범위 15 fps – 60 fps
입력 픽셀 형식 BGR(ImageBuffer API), NV12(Direct3D API)
출력 픽셀 형식 BGR(ImageBuffer API), BGRA(Direct3D API)

VideoScaler 세션 만들기

다음 예제에서는 VSR 세션을 만드는 방법을 보여줍니다. 먼저 ExecutionProviderCatalog 인스턴스를 가져오고 EnsureAndRegisterCertifiedAsync 를 호출하여 사용 가능한 모델을 로드합니다. VideoScalar 클래스에서 GetReadyState를 호출하여 비디오 스케일러가 프레임을 처리할 준비가 되었는지 확인합니다. 그렇지 않은 경우 EnsureReadyAsync 를 호출하여 비디오 스케일러를 초기화합니다.


private VideoScaler? _videoScaler;

protected override async Task LoadModelAsync(SampleNavigationParameters sampleParams)
{
    try
    {

        var catalog = ExecutionProviderCatalog.GetDefault();
        await catalog.EnsureAndRegisterCertifiedAsync();

        var readyState = VideoScaler.GetReadyState();
        if (readyState == AIFeatureReadyState.NotReady)
        {
            var operation = await VideoScaler.EnsureReadyAsync();

            if (operation.Status != AIFeatureReadyResultState.Success)
            {
                ShowException(null, "Video Scaler is not available.");
            }
        }

        _videoScaler = await VideoScaler.CreateAsync();
    }
    catch (Exception ex)
    {
        ShowException(ex, "Failed to load model.");
    }

    sampleParams.NotifyCompletion();
}

VideoFrame 크기 조정

다음 코드 예제에서는 VideoScaler.ScaleFrame 메서드를 사용하여 VideoFrame 개체에 포함된 이미지 데이터의 크기를 조정합니다. MediaFrameReader 클래스를 사용하여 카메라에서 VideoFrame을 가져올 수 있습니다. 자세한 내용은 MediaFrameReader를 사용하여 미디어 프레임 처리를 참조하세요. WinUI 커뮤니티 도구 키트 CameraPreview 컨트롤을 사용하여 카메라에서 VideoFrame 개체를 가져올 수도 있습니다.

다음으로, 입력 비디오 프레임에서 Direct3DSurface 를 가져오고 업 스케일링의 출력을 위해 다른 Direct3DSurface 를 만듭니다. VideoScaler.ScaleFrame 은 프레임을 업스케일링하기 위해 호출됩니다. 이 예제에서는 앱의 UI에 있는 이미지 컨트롤이 업스케일 프레임으로 업데이트됩니다.

 private async Task ProcessFrame(VideoFrame videoFrame)
{
    // Process the frame with super resolution model
    var processedBitmap = await Task.Run(async () =>
    {
        int width = 0;
        int height = 0;
        var inputD3dSurface = videoFrame.Direct3DSurface;
        if (inputD3dSurface != null)
        {
            Debug.Assert(inputD3dSurface.Description.Format == Windows.Graphics.DirectX.DirectXPixelFormat.NV12, "input in NV12 format");
            width = inputD3dSurface.Description.Width;
            height = inputD3dSurface.Description.Height;
        }
        else
        {
            var softwareBitmap = videoFrame.SoftwareBitmap;
            if (softwareBitmap == null)
            {
                return null;
            }

            Debug.Assert(softwareBitmap.BitmapPixelFormat == BitmapPixelFormat.Nv12, "input in NV12 format");

            width = softwareBitmap.PixelWidth;
            height = softwareBitmap.PixelHeight;
        }

        try
        {
            if (inputD3dSurface == null)
            {
                // Create Direct3D11-backed VideoFrame for input
                using var inputVideoFrame = VideoFrame.CreateAsDirect3D11SurfaceBacked(
                    Windows.Graphics.DirectX.DirectXPixelFormat.NV12,
                    width,
                    height);

                if (inputVideoFrame.Direct3DSurface == null)
                {
                    return null;
                }

                // Copy the software bitmap to the Direct3D-backed frame
                await videoFrame.CopyToAsync(inputVideoFrame);

                inputD3dSurface = inputVideoFrame.Direct3DSurface;
            }

            // Create or resize output surface (BGRA8 format for display)
            if (_outputD3dSurface == null || _outputWidth != width || _outputHeight != height)
            {
                _outputD3dSurface?.Dispose();

                // DXGI_FORMAT_B8G8R8A8_UNORM = 87
                _outputD3dSurface = Direct3DExtensions.CreateDirect3DSurface(87, width, height);
                _outputWidth = width;
                _outputHeight = height;
            }

            // Scale the frame using VideoScaler
            var result = _videoScaler!.ScaleFrame(inputD3dSurface, _outputD3dSurface, new VideoScalerOptions());

            if (result.Status == ScaleFrameStatus.Success)
            {
                var outputBitmap = await SoftwareBitmap.CreateCopyFromSurfaceAsync(
                    _outputD3dSurface,
                    BitmapAlphaMode.Premultiplied);

                return outputBitmap;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"ProcessFrame error: {ex.Message}");
        }

        return null;
    });

    if (processedBitmap == null)
    {
        return;
    }

    DispatcherQueue.TryEnqueue(async () =>
    {
        using (processedBitmap)
        {
            var source = new SoftwareBitmapSource();
            await source.SetBitmapAsync(processedBitmap);
            ProcessedVideoImage.Source = source;
        }
    });
}

ImageBuffer를 사용하여 SoftwareBitmap 크기 조정

다음 코드 예제에서는 VideoScalar 클래스를 사용하여 SoftwareBitmap의 크기를 조정하는 방법을 보여 줍니다. 이 예제에서는 VSR API의 일반적인 사용을 나타내지 않습니다. Direct3D를 사용하는 것보다 성능이 떨어집니다. 그러나 이 예제를 사용하여 카메라 또는 비디오 스트리밍 파이프라인을 설정하지 않고 VSR API를 실험할 수 있습니다. ImageBuffer를 사용할 때 비디오 스케일러에 BGR8이 필요하므로 제공된 SoftwareBitmap의 픽셀 형식을 변환하려면 일부 도우미 메서드가 필요합니다.

이 문서의 예제 코드는 Windows AI API 샘플의 VSR 구성 요소를 기반으로 합니다.

    public SoftwareBitmap ScaleVideoFrame(SoftwareBitmap inputFrame)
    {
        ImageBuffer inputImageBuffer = SoftwareBitmapExtensions.ConvertToBgr8ImageBuffer(inputFrame);
        var size = (uint)(inputFrame.PixelWidth * inputFrame.PixelHeight * 3);
        IBuffer outputBuffer = new global::Windows.Storage.Streams.Buffer(size);
        outputBuffer.Length = size;
        ImageBuffer outputImageBuffer = ImageBuffer.CreateForBuffer(
            outputBuffer,
            ImageBufferPixelFormat.Bgr8,
            inputFrame.PixelWidth,
            inputFrame.PixelHeight,
            inputFrame.PixelWidth * 3);
        var result = Session.ScaleFrame(inputImageBuffer, outputImageBuffer, new VideoScalerOptions());
        if (result.Status != ScaleFrameStatus.Success)
        {
            throw new Exception($"Failed to scale video frame: {result.Status}");
        }

        return SoftwareBitmapExtensions.ConvertBgr8ImageBufferToBgra8SoftwareBitmap(outputImageBuffer);
    }

소프트웨어 비트맵 확장 메서드

다음 도우미 메서드는 비디오 스칼라의 입력 및 출력 요구 사항에 맞게 BGRA8BGR8 형식 간에 SoftwareBitmap을 변환합니다.

public static ImageBuffer ConvertToBgr8ImageBuffer(SoftwareBitmap input)
    {
        var bgraBitmap = input;
        if (input.BitmapPixelFormat != BitmapPixelFormat.Bgra8)
        {
            bgraBitmap = SoftwareBitmap.Convert(input, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
        }

        int width = bgraBitmap.PixelWidth;
        int height = bgraBitmap.PixelHeight;

        byte[] bgraBuffer = new byte[width * height * 4];
        bgraBitmap.CopyToBuffer(bgraBuffer.AsBuffer());

        byte[] bgrBuffer = new byte[width * height * 3];
        for (int i = 0, j = 0; i < bgraBuffer.Length; i += 4, j += 3)
        {
            bgrBuffer[j] = bgraBuffer[i];
            bgrBuffer[j + 1] = bgraBuffer[i + 1];
            bgrBuffer[j + 2] = bgraBuffer[i + 2];
        }

        return ImageBuffer.CreateForBuffer(
            bgrBuffer.AsBuffer(),
            ImageBufferPixelFormat.Bgr8,
            width,
            height,
            width * 3);
    }

    public static SoftwareBitmap ConvertBgr8ImageBufferToBgra8SoftwareBitmap(ImageBuffer bgrImageBuffer)
    {
        if (bgrImageBuffer.PixelFormat != ImageBufferPixelFormat.Bgr8)
        {
            throw new ArgumentException("Input ImageBuffer must be in Bgr8 format");
        }

        int width = bgrImageBuffer.PixelWidth;
        int height = bgrImageBuffer.PixelHeight;

        // Get BGR data from ImageBuffer
        byte[] bgrBuffer = new byte[width * height * 3];
        bgrImageBuffer.CopyToByteArray(bgrBuffer);

        // Create BGRA buffer (4 bytes per pixel)
        byte[] bgraBuffer = new byte[width * height * 4];

        for (int i = 0, j = 0; i < bgrBuffer.Length; i += 3, j += 4)
        {
            bgraBuffer[j] = bgrBuffer[i];     // B
            bgraBuffer[j + 1] = bgrBuffer[i + 1]; // G
            bgraBuffer[j + 2] = bgrBuffer[i + 2]; // R
            bgraBuffer[j + 3] = 255;              // A (full opacity)
        }

        // Create SoftwareBitmap and copy data
        var softwareBitmap = new SoftwareBitmap(
            BitmapPixelFormat.Bgra8,
            width,
            height,
            BitmapAlphaMode.Premultiplied);

        softwareBitmap.CopyFromBuffer(bgraBuffer.AsBuffer());

        return softwareBitmap;
    }

책임 있는 인공지능

다음 단계의 조합을 사용하여 이러한 이미징 API가 신뢰할 수 있고 안전하며 책임감 있게 빌드되도록 했습니다. 앱에서 AI 기능을 구현할 때 Windows의 책임 있는 생성 AI 개발에 설명된 모범 사례를 검토하는 것이 좋습니다.

이러한 VSR API는 ML(Machine Learning) 모델을 사용하며, 화상 통화 및 회의 앱과 같은 시나리오와 사람의 얼굴 말하기를 특징으로 하는 소셜 및 짧은 형식의 비디오를 위해 특별히 설계되었습니다. 따라서 다음 시나리오에서는 이러한 API를 이미지에 사용하지 않는 것이 좋습니다.

  • 이미지에 잠재적으로 민감한 콘텐츠와 부정확한 설명이 포함된 경우 플래그, 지도, 지구본, 문화 기호 또는 종교적 기호와 같이 논란의 여지가 있을 수 있습니다.
  • 의학적 조언이나 진단, 법적 콘텐츠 또는 재무 문서와 같은 정확한 설명이 중요한 경우

참고하십시오