Я собираю данные 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, я не отношусь к цвету и ИК-изображениям по-другому. Как собрать среднюю окраску пикселей ИК-кадра?