Попытка преобразования YU12 в YUYV привела к зашумленному изображению

Я пытаюсь использовать библиотеку 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 и длина data462848. Отмечу, что длина data немного странная, как и ожидалось 460800, как указано при запуске v4l2-ctl -d /dev/video0 --all, поэтому я не уверен, откуда берутся дополнительные 2048 байты. Я думал, что это может быть какое-то дополнение, но я не уверен на 100%.

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

Christoph Rackwitz 16.05.2024 08:02

@ChristophRackwitz - Спасибо, что указали на это. Я продолжу расследование, так как не учел этого. Однако приведенный выше код на самом деле не манипулирует значениями пикселей, это делается в другом месте библиотеки. Формат пикселей видеопотока моей веб-камеры — YUYV, и он отлично работает с этой библиотекой, что заставило меня поверить, что проблема заключалась в преобразовании из YU12 в YUYV, а не в самой библиотеке. Но я посмотрю там повнимательнее.

Ajay Pillay 16.05.2024 08:19

Вероятно, между плоскостями Y и плоскостью U (и, возможно, также между плоскостями U и V) есть некоторое заполнение. Как следствие, вы считываете значения U, V для неправильного пикселя → они не слишком заметны в однородных областях, где оба пикселя имеют одинаковый цвет, но возникают проблемы вблизи краев, где пиксели существенно различаются.

Jmb 16.05.2024 08:31

Я сильно сомневаюсь, что это связано с путаницей с самолетами YUV. искажение имеет явные проблемы с R/G/B (обертывание по нижнему значению составляет около 255 на каждый компонент). потенциально дело в "диапазоне" (полный диапазон или ерунда 16-235). видео исторически было чрезвычайно безумным, больше всего затрагивая типы yuv.

Christoph Rackwitz 16.05.2024 08:42

@ChristophRackwitz такого рода переполнение может произойти во время преобразования YUV → RGB, если компоненты U,V слишком велики по сравнению с компонентом Y, что может произойти, если вы попытаетесь объединить компоненты U,V яркого пикселя с темным Y.

Jmb 16.05.2024 09:21

Есть ли у вас какие-либо советы о том, как выяснить, где именно находится отступ? Я попробовал сделать несколько вещей, например, игнорировать 1024 байта между слоями Y и U (и аналогичным образом между U и V) и игнорировать 2048 байтов между Y и U без заполнения между U и V и различные комбинации этого. Я считаю, что где-то в последнем абзаце ОП есть 2048 байт заполнения. Я также проверил массив необработанных данных вручную и не смог найти никаких значений байтов, указывающих на заполнение (я предполагал, что это будет байт со значением 0/255). В Интернете мне тоже ничего не удалось найти.

Ajay Pillay 16.05.2024 10:12
kernel.org/doc/html/v4.9/media/uapi/v4l/pixfmt-yuv420.html упоминает, как его можно дополнить, но опять же я не понимаю, как определить, сколько байтов заполнения будет после каждую строку Y, кроме чистого метода проб и ошибок, что не идеально.
Ajay Pillay 16.05.2024 10:30
0
7
78
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В функции 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 было правильным. Я был на старой ветке этого ящика, в которой не было зажима. При пережатии изображение выглядит как положено.

Ajay Pillay 16.05.2024 19:40

Другие вопросы по теме