Я пытаюсь использовать библиотеку Rust nokhwa для захвата изображений с помощью камеры телефона Android. Я подключил свой телефон Android через USB к ноутбуку и использую Droidcam, чтобы он работал в качестве веб-камеры.
Камера моего телефона отправляет изображение в формате YU12, который библиотека изначально не поддерживает. Я пытаюсь добавить его поддержку, преобразовав поток байтов из YU12 в YUYV.
Это функция, которая пытается выполнить преобразование:
pub fn yu12_to_yuyv(resolution: Resolution, data: &[u8]) -> Vec<u8> {
// Calculate the sizes of the Y, U, and V planes
let width = resolution.width_x as usize;
let height = resolution.height_y as usize;
let size = width * height;
let u_size = (width / 2) * (height / 2);
let v_size = u_size;
// Extract Y, U, and V planes from the input data
let y_plane = &data[0..size];
let u_plane = &data[size..(size + u_size)];
let v_plane = &data[(size + u_size)..(size + u_size + v_size)];
// Create a vector to hold the YUYV data
let mut yuyv = Vec::with_capacity(size * 2);
// Iterate over the image in 2x1 pixel blocks
for y in 0..height {
for x in (0..width).step_by(2) {
// Calculate positions in the Y, U, and V planes
let y_index1 = y * width + x;
let y_index2 = y * width + x + 1;
let uv_index = (y / 2) * (width / 2) + (x / 2);
// Read Y, U, and V values
let y1 = y_plane[y_index1];
let y2 = y_plane[y_index2];
let u = u_plane[uv_index];
let v = v_plane[uv_index];
// Append YUYV data (2 pixels)
yuyv.push(y1);
yuyv.push(u);
yuyv.push(y2);
yuyv.push(v);
}
}
yuyv
}
Однако результирующее изображение выглядит следующим образом:
Конечно, это выглядит в основном правильно, за исключением того, что кажется шумом. Что здесь может быть не так, учитывая, что большая часть изображения выглядит нормально? Для большего контекста: ширина — 640, высота — 480 и длина data — 462848. Отмечу, что длина data немного странная, как и ожидалось 460800, как указано при запуске v4l2-ctl -d /dev/video0 --all, поэтому я не уверен, откуда берутся дополнительные 2048 байты. Я думал, что это может быть какое-то дополнение, но я не уверен на 100%.
@ChristophRackwitz - Спасибо, что указали на это. Я продолжу расследование, так как не учел этого. Однако приведенный выше код на самом деле не манипулирует значениями пикселей, это делается в другом месте библиотеки. Формат пикселей видеопотока моей веб-камеры — YUYV, и он отлично работает с этой библиотекой, что заставило меня поверить, что проблема заключалась в преобразовании из YU12 в YUYV, а не в самой библиотеке. Но я посмотрю там повнимательнее.
Вероятно, между плоскостями Y и плоскостью U (и, возможно, также между плоскостями U и V) есть некоторое заполнение. Как следствие, вы считываете значения U, V для неправильного пикселя → они не слишком заметны в однородных областях, где оба пикселя имеют одинаковый цвет, но возникают проблемы вблизи краев, где пиксели существенно различаются.
Я сильно сомневаюсь, что это связано с путаницей с самолетами YUV. искажение имеет явные проблемы с R/G/B (обертывание по нижнему значению составляет около 255 на каждый компонент). потенциально дело в "диапазоне" (полный диапазон или ерунда 16-235). видео исторически было чрезвычайно безумным, больше всего затрагивая типы yuv.
@ChristophRackwitz такого рода переполнение может произойти во время преобразования YUV → RGB, если компоненты U,V слишком велики по сравнению с компонентом Y, что может произойти, если вы попытаетесь объединить компоненты U,V яркого пикселя с темным Y.
Есть ли у вас какие-либо советы о том, как выяснить, где именно находится отступ? Я попробовал сделать несколько вещей, например, игнорировать 1024 байта между слоями Y и U (и аналогичным образом между U и V) и игнорировать 2048 байтов между Y и U без заполнения между U и V и различные комбинации этого. Я считаю, что где-то в последнем абзаце ОП есть 2048 байт заполнения. Я также проверил массив необработанных данных вручную и не смог найти никаких значений байтов, указывающих на заполнение (я предполагал, что это будет байт со значением 0/255). В Интернете мне тоже ничего не удалось найти.
В функции buf_yuyv422_to_rgb()nokhwa-core мы видим это преобразование
let r0 = y0 + 1.370_705 * (v - 128.);
let g0 = y0 - 0.698_001 * (v - 128.) - 0.337_633 * (u - 128.);
let b0 = y0 + 1.732_446 * (u - 128.);
и полученные компоненты RBG сохраняются напрямую вот так
[r0 as u8, g0 as u8, b0 as u8, ...]
Игра со значениями полного диапазона (0..=255) y0, u и v приводит к тому, что некоторые значения r0, g0 и b0 выходят за пределы диапазона 0..=255, тогда приведение as u8 совершенно неверно (переход).
С другой стороны, функция yuyv444_to_rgb() (чуть ниже в том же файле) использует соответствующие операции .clamp(0, 255), чтобы предотвратить это (хотя формулы разные — целочисленные аппроксимации).
Может быть, вам следует попытаться самостоятельно вычислить компоненты RGB, применив соответствующее ограничение, а затем напрямую предоставить буфер RGB вместо YUYV?
Спасибо, что указали на это. Оказывается, преобразование YU12 в YUYV было правильным. Я был на старой ветке этого ящика, в которой не было зажима. При пережатии изображение выглядит как положено.
возможно, это целочисленное переполнение/недополнение/зацикливание. отладьте свой код. посмотрите на расчет для одного конкретного неисправного пикселя.