Как собрать данные RGBA из пикселей инфракрасного кадра Microsoft Kinect (SDK 2.0)? - PullRequest
0 голосов
/ 22 января 2020

Я собираю данные RGBA из каналов Kinect ColorFrame и IRFrame в приложении WPF и измеряю средний цвет каждого кадра (значения R в пикселях среднего кадра, значения G и значения B, игнорируя A, потому что я меня не беспокоит непрозрачность). Это не было проблемой с ColorFrame (PixelFormats.Bgra32), но ИК-кадр доставляет мне проблемы, так как результирующий WriteableBitmap находится в PixelFormats.Gray16, с которым я менее знаком.

I у меня есть две IR-битовые карты, которые я использую: _irBitmap (полное изображение IRFrame) и меньшая битовая карта, которая копирует прямоугольник пикселей из _irBitmap.

Вот событие, где я собираю IR-кадр изначально. (игнорируйте, что он использует MultiSourceFrame, я удалил другой код для удобства чтения здесь).

private void Reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
    using (InfraredFrame irFrame = multiFrame.InfraredFrameReference.AcquireFrame())
    {
        HandleFrame(irFrame);
    }
}

Здесь кадр записывается в карту WriteableBitmap, которую я отображаю в WPF.

private void HandleFrame(InfraredFrame irFrame)
{
    if (irFrame == null)
    {
        return;
    }

    ushort[] irData = new ushort[_irDisplayHeight * _irDisplayWidth];
    irFrame.CopyFrameDataToArray(irData);

    _irBitmap.Lock();

    var backBuffer = _irBitmap.BackBuffer;

    long total = 0;
    for (int i = 0; i < irData.Length; ++i)
    {
        total += irData[i];
    }
    var average = ((double)total / irData.Length);
    var sumOfSquaresOfDifferences = irData.Select(value => (value - average) * (value - average)).Sum();
    var standardDeviation = Math.Sqrt((double)sumOfSquaresOfDifferences / irData.Length);
    if (standardDeviation <= 0)
    {
        standardDeviation = 1;
    }
    if (standardDeviation > average)
    {
        standardDeviation = average;   // prevent unsigned overflow
    }
    ushort minimumValue = (ushort)(average - standardDeviation);

    if (Math.Abs(average + standardDeviation - ushort.MaxValue) < 0.0001)
    {
        standardDeviation = 0;
    }
    double maximumValue = average + standardDeviation;

    if (minimumValue <= 0)
    {
        minimumValue = 0;
    }
    maximumValue -= minimumValue;

    if (maximumValue > 65535)
    {
        maximumValue = 65535;
    }

    for (int i = 0; i < irData.Length; ++i)
    {
        int newValue = 0;
        if (minimumValue < irData[i])
        {
            newValue = irData[i] - minimumValue;

            if (newValue > maximumValue)
            {
                _infraredBuffer[i] = 255;
            }
            else
            {
                _infraredBuffer[i] = (byte)((newValue / maximumValue) * 255);
            }
        }
        else
        {
            _infraredBuffer[i] = 0;
        }

    }

    Marshal.Copy(_infraredBuffer, 0, backBuffer, _infraredBuffer.Length);
    _irBitmap.AddDirtyRect(new Int32Rect(0, 0, _irBitmap.PixelWidth, _irBitmap.PixelHeight));
    _irBitmap.Unlock();
}

При этом _irBitmap обновляется в реальном времени и выглядит как черно-белый. белая камера подачи.

После этого я создаю Rect (произвольное местоположение) и использую его для создания меньшего растрового изображения, пиксели которого я буду перебирать позже.

public static WriteableBitmap GetIRBitmap(
    WriteableBitmap bitmapSource,
    SaturationSquare ss)
{
    var resultWidth = (int)(ss.TRInfraredPointNormalized.X - ss.TLInfraredPointNormalized.X);
    var resultHeight = (int)(ss.BRInfraredPointNormalized.Y - ss.TRInfraredPointNormalized.Y);
    var resultBitmap = new WriteableBitmap(resultWidth, resultHeight, 96.0, 96.0, PixelFormats.Gray16, null);

    // The location to be copied from the full IR bitmap
    Rect smallRectSource = new Rect(
        ss.TLInfraredPointNormalized.X,
        ss.TLInfraredPointNormalized.Y,
        ss.TRInfraredPointNormalized.X - ss.TLInfraredPointNormalized.X,
        ss.BRInfraredPointNormalized.Y - ss.TRInfraredPointNormalized.Y);

    // The location on the new bitmap to copy to
    Rect topLeftCorner = new Rect(
        0,
        0,
        fss.TRInfraredPointNormalized.X - fss.TLInfraredPointNormalized.X,
        fss.BRInfraredPointNormalized.Y - fss.TRInfraredPointNormalized.Y);

    using (resultBitmap.GetBitmapContext())
    {
        using (bitmapSource.GetBitmapContext())
        {
            // Copies smallRectSource from bitmapSource to the topLeftCorner of resultBitmap
            resultBitmap.Blit(
                topLeftCorner,
                bitmapSource,
                smallRectSource,
                WriteableBitmapExtensions.BlendMode.Additive);
        }
    }

    // returns the smaller bitmap, ready for iterating through
    return resultBitmap;
}

Наконец, я перебираю это маленькое растровое изображение, проверяя окраску каждого пикселя и добавляя его в класс, который собирает среднее значение каждого цветового байта.

private static void UpdateIRStats(WriteableBitmap bitmap, SaturationStatistics ss)
{
    bitmap.Lock();
    var buffer = bitmap.BackBuffer;
    byte[] copy = new byte[bitmap.PixelWidth * bitmap.PixelHeight * 4];

    bitmap.ForEach((x, y, color) =>
    {
        // When iterating through the RGBA bitmap, this has R, G, B values ranging from 0-255.
        // When iterating through the IR bitmap, all R, G, B values are consistently 0.
        ss.AddIRPixel(color);

        return color;
    });

    Marshal.Copy(copy, 0, buffer, bitmap.PixelWidth * bitmap.PixelHeight * 4);
    bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
    bitmap.Unlock();
}

Несмотря на случаи, когда меньший растровый рисунок в основном белый, полученные пиксели всегда возвращаются как # 00000000 (чистый черный, где R = 0, G = 0, B = 0). Я не могу изобразить, как средняя ИК-окраска меняется со временем, хотя я знаю, что по крайней мере некоторые из этих пикселей должны быть белыми (255, 255, 255).

За исключением случаев сбора необработанных цветных кадров и IRFrames, я не отношусь к цвету и ИК-изображениям по-другому. Как собрать среднюю окраску пикселей ИК-кадра?

...