Почему WriteableBitmap BackBufferStride отличается от EmguCV Mat Step - PullRequest
1 голос
/ 24 апреля 2020

Используя netcoreapp3.1 и EmguCV 3.4.1, я создаю, с одной стороны, WriteableBitmap, а с другой - EmguCV Mat. Оба имеют одинаковый размер 2793 x 2585

var wb = new WriteableBitmap(2793, 2585, 96, 96, PixelFormats.Bgr24, null);
int wbStride = wb.BackBufferStride; //8380 

var m = new Mat(2585, 2793, DepthType.Cv8U, 3);
int matStride = m.Step; //8379

Для WriteableBitmap BackBufferStride = 8380, но для Mat я получаю Step = 8379. Я обнаружил, что существуют две разные формулы, часто используемые для вычисления шага:

a) Stride = ((ширина * bitsPerPixel + 31) & ~ 31) >> 3;

b) Stride = (ширина * bitsPerPixel + 7) / 8

Формула a) приводит к получению значения для WriteableBitmap BackBufferStride и формулы b) к значению EmguCV Mat.

Почему бега отличаются для одинаковой ширины и высоты? Какая формула правильная?

Ответы [ 2 ]

1 голос
/ 25 апреля 2020

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

Поскольку обе ваши формулы удовлетворяют этому требованию, обе верны: первая добавляется в память для каждой строки пикселей таким образом, что он выравнивается по 32-битной границе, а второй выравнивает каждую строку по 8-битной границе. Но оба из них приводят к шагу, который достаточно большой, чтобы вместить весь ряд пикселей.

32-битовые выравнивания, вероятно, все еще используются в. пиксели копируются в памяти.

EmguCV Mat, с другой стороны, является чем-то совершенно другим; это матрица общего назначения, и она не должна соответствовать тем соглашениям о растровых изображениях, которые используются. NET. Документация на этой странице предполагает, что никакие байты заполнения или Формула B не являются поведением по умолчанию. Эта перегрузка конструктора позволяет указать пользовательский шаг, однако вам необходимо указать его на заранее определенный буфер:

var buffer = Marshal.AllocHGlobal(2585 * 8380);
var m = new Mat(2585, 2793, DepthType.Cv8U, 3, buffer, 8380);
int matStride = m.Step; //8380
Marshal.FreeHGlobal(buffer);
1 голос
/ 25 апреля 2020

Не уверен, почему именно BackBufferStride возвращает это значение.

Однако любое значение, большее или равное минимальный шаг (width * bpp + 7) / 8, действительно для шага растрового изображения. Требуется только, чтобы шаг был достаточно большим, то есть на каждой строке сканирования было достаточно байтов для хранения всех битов.

Вы можете попробовать следующий код и добавить к шагу все, что захотите, например, stride += 100;. Просто убедитесь, что шаг также используется для расчета размера пиксельного буфера.

var width = 2793;
var height = 2585;
var stride = (width * PixelFormats.Bgr24.BitsPerPixel + 7) / 8; // 8379

stride += 100;

var buffer = new byte[stride * height];

for (int y = 0; y < height; y++)
{
    for (int x = y % 10; x < width; x += 10)
    {
        buffer[stride * y + 3 * x + 0] = 0xFF;
        buffer[stride * y + 3 * x + 1] = 0xFF;
        buffer[stride * y + 3 * x + 2] = 0xFF;
    }
}

var bitmap = BitmapSource.Create(
    width, height, 96, 96, PixelFormats.Bgr24, null, buffer, stride);
...