2015-01-21 4 views
0

녹이에 JPEG 디코더/인코더를 쓰고 있는데 RGB ↔ YCbCr 변환에 문제가 있습니다.SIMD 벡터를 사용하는 RGB에서 YCbCr로 일부 데이터가 손실됩니다.

내 코드 : (주석) 어떤 이유로 부스 테스트를위한

use std::simd::f32x4; 

fn clamp<T>(val: T, min: T, max: T) -> T 
where T: PartialOrd { 
    if val < min { min } 
    else if max < val { max } 
    else { val } 
} 

// in oryginal code there are 2 methods, one for processors with SSE3 and for rest 
// both do the same and give the same results 
pub fn sum_f32x4(f32x4(a, b, c, d): f32x4) -> f32 { 
    a + b + c + d 
} 

pub fn rgb_to_ycbcr(r: u8, g: u8, b: u8) -> (u8, u8, u8) { 
    let rgb = f32x4(r as f32, g as f32, b as f32, 1.0); 
    let y = sum_f32x4(rgb * f32x4(0.2990, 0.5870, 0.1140, 0.0)); 
    let cb = sum_f32x4(rgb * f32x4(-0.1687, -0.3313, 0.5000, 128.0)); 
    let cr = sum_f32x4(rgb * f32x4(0.5000, -0.4187, -0.0813, 128.0)); 

    (y as u8, cb as u8, cr as u8) 
} 

pub fn ycbcr_to_rgb(y: u8, cb: u8, cr: u8) -> (u8, u8, u8) { 
    let ycbcr = f32x4(y as f32, cb as f32 - 128.0f32, cr as f32 - 128.0f32, 0.0); 
    let r = sum_f32x4(ycbcr * f32x4(1.0, 0.00000, 1.40200, 0.0)); 
    let g = sum_f32x4(ycbcr * f32x4(1.0, -0.34414, -0.71414, 0.0)); 
    let b = sum_f32x4(ycbcr * f32x4(1.0, 1.77200, 0.00000, 0.0)); 

    (clamp(r, 0., 255.) as u8, clamp(g, 0., 255.) as u8, clamp(b, 0., 255.) as u8) 
} 

fn main() { 
    assert_eq!(rgb_to_ycbcr( 0, 71, 171), (61, 189, 84)); 
    // assert_eq!(rgb_to_ycbcr( 0, 71, 169), (61, 189, 84)); // will fail 
    // for some reason we always lose data on blue channel 
    assert_eq!(ycbcr_to_rgb(61, 189, 84), ( 0, 71, 169)); 
} 

을 전달합니다. 나는 오히려 그 중 적어도 하나가 실패 할 것이라고 기대하고있다. 내가 잘못? 적어도 어느 시점에서는 중단해야하지만, jpeg::color::utils::rgb_to_ycbcr(0, 71, 171)jpeg::color::utils::rgb_to_ycbcr(0, 71, 169)으로 변경하면 YCbCr 값이 변경되어 테스트가 실패하므로 영원히 파란색 채널이 손실됩니다.

+0

완료. 이제 완전하고 실행 가능합니다. – Hauleth

+0

"파란 채널을 잃어 버림"이란 무엇을 의미합니까? 그리고 왜'rgb_to_ycbcr'에 대해 동일한 출력을주기 위해 입력을 변경하려고합니까? – huon

+0

RGB'(0, 71, 171)'에서 YCbCr'(61, 189, 84)'로 그리고 RGB'(0, 71, 169)'로 다시 변환하면 청색 채널 값이 달라지며 내가 설정 한 가치는 항상 다릅니다 (나는 "멈추지 않는"가치를 발견하지 못했습니다). – Hauleth

답변

3

@dbaupp는 round를 사용하도록 제안에 관에 못을 넣어 : 나는 또한 Wikipedia page에서 rgb_to_ycbcr에서 값의 정밀도를 증가

#![allow(unstable)] 

use std::simd::{f32x4}; 
use std::num::Float; 

fn clamp(val: f32) -> u8 { 
    if val < 0.0 { 0 } 
    else if val > 255.0 { 255 } 
    else { val.round() as u8 } 
} 

fn sum_f32x4(v: f32x4) -> f32 { 
    v.0 + v.1 + v.2 + v.3 
} 

pub fn rgb_to_ycbcr((r, g, b): (u8, u8, u8)) -> (u8, u8, u8) { 
    let rgb = f32x4(r as f32, g as f32, b as f32, 1.0); 
    let y = sum_f32x4(rgb * f32x4(0.299000, 0.587000, 0.114000, 0.0)); 
    let cb = sum_f32x4(rgb * f32x4(-0.168736, -0.331264, 0.500000, 128.0)); 
    let cr = sum_f32x4(rgb * f32x4(0.500000, -0.418688, -0.081312, 128.0)); 

    (clamp(y), clamp(cb), clamp(cr)) 
} 

pub fn ycbcr_to_rgb((y, cb, cr): (u8, u8, u8)) -> (u8, u8, u8) { 
    let ycbcr = f32x4(y as f32, cb as f32 - 128.0f32, cr as f32 - 128.0f32, 0.0); 
    let r = sum_f32x4(ycbcr * f32x4(1.0, 0.00000, 1.40200, 0.0)); 
    let g = sum_f32x4(ycbcr * f32x4(1.0, -0.34414, -0.71414, 0.0)); 
    let b = sum_f32x4(ycbcr * f32x4(1.0, 1.77200, 0.00000, 0.0)); 

    (clamp(r), clamp(g), clamp(b)) 
} 

fn main() { 
    let mut rgb = (0, 71, 16); 
    println!("{:?}", rgb); 

    for _ in 0..100 { 
     let yuv = rgb_to_ycbcr(rgb); 
     rgb = ycbcr_to_rgb(yuv); 

     println!("{:?}", rgb); 
     } 
} 

참고. 나 또한 clampround을 호출 할 수 있습니다. 이제 출력된다 : 전체 루프 반복의 마지막 값으로

(0u8, 71u8, 16u8) 
(1u8, 72u8, 16u8) 
(1u8, 72u8, 16u8) 

.