171 lines
6.8 KiB
Plaintext
171 lines
6.8 KiB
Plaintext
🔴 문제 1: YUV→RGB 변환 최적화 방안
|
|
|
|
방안 1: 룩업 테이블 + 라인 단위 처리
|
|
|
|
// 초기화 시 한 번만 계산하는 룩업 테이블
|
|
static int yuv_table_r[256]; // V → R 변환 테이블
|
|
static int yuv_table_g_u[256]; // U → G 변환 테이블
|
|
static int yuv_table_g_v[256]; // V → G 변환 테이블
|
|
static int yuv_table_b[256]; // U → B 변환 테이블
|
|
|
|
// 최적화된 변환 함수
|
|
void ConvertYUV420PToRGB24_Optimized() {
|
|
for (uint32_t y = 0; y < height; ++y) {
|
|
const uint8_t* y_row = y_plane + (y * y_stride);
|
|
const uint8_t* u_row = u_plane + ((y / 2) * u_stride);
|
|
const uint8_t* v_row = v_plane + ((y / 2) * v_stride);
|
|
uint8_t* rgb_row = rgb_data + (y * rgb_stride);
|
|
|
|
// 라인 단위로 2픽셀씩 처리 (YUV420P 특성 활용)
|
|
for (uint32_t x = 0; x < width; x += 2) {
|
|
uint8_t U = u_row[x / 2];
|
|
uint8_t V = v_row[x / 2];
|
|
|
|
// 룩업 테이블로 변환 계수 조회 (곱셈 대신)
|
|
int r_offset = yuv_table_r[V];
|
|
int g_offset = yuv_table_g_u[U] + yuv_table_g_v[V];
|
|
int b_offset = yuv_table_b[U];
|
|
|
|
// 2픽셀 동시 처리
|
|
for (int px = 0; px < 2 && (x + px) < width; ++px) {
|
|
int Y = y_row[x + px] - 16;
|
|
Y = (Y * 298) >> 8; // 비트 시프트로 나눗셈 대체
|
|
|
|
rgb_row[(x + px) * 3 + 0] = clamp(Y + r_offset);
|
|
rgb_row[(x + px) * 3 + 1] = clamp(Y + g_offset);
|
|
rgb_row[(x + px) * 3 + 2] = clamp(Y + b_offset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
성능 향상: 기존 대비 3-4배 빠름 (룩업 테이블 + 라인 최적화)
|
|
|
|
방안 2: SIMD (SSE/AVX) 벡터화
|
|
|
|
#include <immintrin.h> // AVX2
|
|
|
|
void ConvertYUV420PToRGB24_SIMD() {
|
|
for (uint32_t y = 0; y < height; ++y) {
|
|
// 8픽셀을 한 번에 처리 (AVX2 256bit)
|
|
for (uint32_t x = 0; x < width; x += 8) {
|
|
// Y값 8개 로드
|
|
__m256i y_vec = _mm256_loadu_si256((__m256i*)(y_plane + y * y_stride + x));
|
|
|
|
// U, V값 4개씩 로드 후 중복 확장
|
|
__m128i uv_4 = _mm_loadu_si128((__m128i*)(u_plane + (y/2) * u_stride + x/2));
|
|
__m256i u_vec = _mm256_unpacklo_epi8(uv_4, uv_4); // 중복 확장
|
|
|
|
// 벡터화된 YUV→RGB 변환
|
|
__m256i r_vec = yuv_to_rgb_simd(y_vec, u_vec, v_vec, 0);
|
|
__m256i g_vec = yuv_to_rgb_simd(y_vec, u_vec, v_vec, 1);
|
|
__m256i b_vec = yuv_to_rgb_simd(y_vec, u_vec, v_vec, 2);
|
|
|
|
// 인터리브해서 RGB24 형태로 저장
|
|
store_rgb24_simd(rgb_data + y * rgb_stride + x * 3, r_vec, g_vec, b_vec);
|
|
}
|
|
}
|
|
}
|
|
성능 향상: 기존 대비 8-10배 빠름 (SIMD 병렬 처리)
|
|
|
|
방안 3: 외부 라이브러리 사용
|
|
|
|
// Intel IPP 라이브러리 사용
|
|
#include <ipp.h>
|
|
|
|
void ConvertYUV420PToRGB24_IPP() {
|
|
IppiSize roi = {width, height};
|
|
const Ipp8u* pSrc[3] = {y_plane, u_plane, v_plane};
|
|
int srcStep[3] = {y_stride, u_stride, v_stride};
|
|
|
|
// 한 줄로 고성능 변환
|
|
ippiYUV420ToRGB_8u_P3C3R(pSrc, srcStep, rgb_data, rgb_stride, roi);
|
|
}
|
|
성능 향상: 기존 대비 10-15배 빠름 (최고 최적화)
|
|
|
|
---
|
|
🔴 문제 2: BMP 파일 저장 최적화 방안
|
|
|
|
방안 1: 블록 단위 쓰기 + RGB→BGR 변환 분리
|
|
|
|
void SaveAsBMP_Optimized() {
|
|
// 1. RGB→BGR 변환을 메모리에서 먼저 수행
|
|
std::vector<uint8_t> bgr_buffer(rgb_frame.height * padded_row_size);
|
|
|
|
for (uint32_t y = 0; y < rgb_frame.height; ++y) {
|
|
const uint8_t* src_row = rgb_frame.data.data() + (y * rgb_frame.stride);
|
|
uint8_t* dst_row = bgr_buffer.data() + (y * padded_row_size);
|
|
|
|
// 라인 단위로 RGB→BGR 변환 (패딩 포함)
|
|
for (uint32_t x = 0; x < rgb_frame.width; ++x) {
|
|
dst_row[x * 3 + 0] = src_row[x * 3 + 2]; // B
|
|
dst_row[x * 3 + 1] = src_row[x * 3 + 1]; // G
|
|
dst_row[x * 3 + 2] = src_row[x * 3 + 0]; // R
|
|
}
|
|
|
|
// 패딩 영역 0으로 채움
|
|
memset(dst_row + rgb_frame.width * 3, 0, padding);
|
|
}
|
|
|
|
// 2. 한 번에 블록 쓰기 (621만 번 → 1080번)
|
|
for (int32_t y = rgb_frame.height - 1; y >= 0; --y) {
|
|
const uint8_t* row_data = bgr_buffer.data() + (y * padded_row_size);
|
|
file.write(reinterpret_cast<const char*>(row_data), padded_row_size);
|
|
}
|
|
}
|
|
성능 향상: 기존 대비 50-100배 빠름
|
|
|
|
방안 2: 메모리 매핑 파일 I/O
|
|
|
|
void SaveAsBMP_MemoryMapped() {
|
|
// Windows 메모리 매핑
|
|
HANDLE hFile = CreateFile(file_path.c_str(), GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, total_file_size, NULL);
|
|
uint8_t* mapped_memory = static_cast<uint8_t*>(MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0));
|
|
|
|
// 메모리에 직접 쓰기 (파일 I/O 없음)
|
|
memcpy(mapped_memory, &header, sizeof(header));
|
|
|
|
for (int32_t y = rgb_frame.height - 1; y >= 0; --y) {
|
|
uint8_t* dst = mapped_memory + header_size + (rgb_frame.height - 1 - y) * padded_row_size;
|
|
const uint8_t* src = rgb_frame.data.data() + (y * rgb_frame.stride);
|
|
|
|
// RGB→BGR 변환하면서 직접 복사
|
|
convert_rgb_to_bgr_line(src, dst, rgb_frame.width);
|
|
}
|
|
|
|
UnmapViewOfFile(mapped_memory);
|
|
CloseHandle(hMapping);
|
|
CloseHandle(hFile);
|
|
}
|
|
성능 향상: 기존 대비 100-200배 빠름
|
|
|
|
방안 3: SIMD를 이용한 RGB→BGR 변환
|
|
|
|
void ConvertRGBToBGR_SIMD(const uint8_t* rgb, uint8_t* bgr, size_t pixel_count) {
|
|
const __m256i shuffle_mask = _mm256_setr_epi8(
|
|
2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12, -1,
|
|
2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12, -1
|
|
);
|
|
|
|
for (size_t i = 0; i < pixel_count; i += 32) { // 32바이트씩 처리
|
|
__m256i rgb_data = _mm256_loadu_si256((__m256i*)(rgb + i));
|
|
__m256i bgr_data = _mm256_shuffle_epi8(rgb_data, shuffle_mask);
|
|
_mm256_storeu_si256((__m256i*)(bgr + i), bgr_data);
|
|
}
|
|
}
|
|
성능 향상: 변환 부분만 5-8배 빠름
|
|
|
|
---
|
|
📊 종합 성능 개선 효과 예상
|
|
|
|
| 방안 | YUV→RGB | BMP 저장 | 전체 효과 |
|
|
|--------------|---------|--------|-----------|
|
|
| 현재 | 150ms | 200ms | 350ms/프레임 |
|
|
| 룩업테이블 + 블록쓰기 | 40ms | 4ms | 44ms/프레임 |
|
|
| SIMD + 메모리매핑 | 15ms | 2ms | 17ms/프레임 |
|
|
| 외부라이브러리(IPP) | 10ms | 2ms | 12ms/프레임 |
|
|
|
|
결론: 현재 2.8fps → 30fps 실시간 재생 가능
|
|
|
|
어떤 방안부터 구현해보시겠습니까? |