mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-15 11:49:07 +00:00
cellGem: Fix YUV conversions, implement gain, averaging and basic outlier detection
Some checks failed
Generate Translation Template / Generate Translation Template (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux-aarch64.sh, gcc, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux.sh, gcc, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1, rpcs3/rpcs3-binaries-linux-arm64, /rpcs3/.ci/build-linux-aarch64.sh, clang, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (d812f1254a1157c80fd402f94446310560f54e5f, rpcs3/rpcs3-binaries-linux, /rpcs3/.ci/build-linux.sh, clang, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Has been cancelled
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (51ae32f468089a8169aaf1567de355ff4a3e0842, rpcs3/rpcs3-binaries-mac, .ci/build-mac.sh, Intel) (push) Has been cancelled
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (8e21bdbc40711a3fccd18fbf17b742348b0f4281, rpcs3/rpcs3-binaries-mac-arm64, .ci/build-mac-arm64.sh, Apple Silicon) (push) Has been cancelled
Build RPCS3 / RPCS3 Windows (push) Has been cancelled
Build RPCS3 / RPCS3 Windows Clang (win64, clang, clang64) (push) Has been cancelled
Build RPCS3 / RPCS3 FreeBSD (push) Has been cancelled
Some checks failed
Generate Translation Template / Generate Translation Template (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux-aarch64.sh, gcc, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux.sh, gcc, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1, rpcs3/rpcs3-binaries-linux-arm64, /rpcs3/.ci/build-linux-aarch64.sh, clang, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (d812f1254a1157c80fd402f94446310560f54e5f, rpcs3/rpcs3-binaries-linux, /rpcs3/.ci/build-linux.sh, clang, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Has been cancelled
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (51ae32f468089a8169aaf1567de355ff4a3e0842, rpcs3/rpcs3-binaries-mac, .ci/build-mac.sh, Intel) (push) Has been cancelled
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (8e21bdbc40711a3fccd18fbf17b742348b0f4281, rpcs3/rpcs3-binaries-mac-arm64, .ci/build-mac-arm64.sh, Apple Silicon) (push) Has been cancelled
Build RPCS3 / RPCS3 Windows (push) Has been cancelled
Build RPCS3 / RPCS3 Windows Clang (win64, clang, clang64) (push) Has been cancelled
Build RPCS3 / RPCS3 FreeBSD (push) Has been cancelled
This commit is contained in:
parent
2b0456520e
commit
c3db85c68e
@ -698,22 +698,88 @@ namespace gem
|
||||
{
|
||||
}
|
||||
|
||||
static inline u8 Y(u8 r, u8 g, u8 b) { return static_cast<u8>(0.299f * r + 0.587f * g + 0.114f * b); }
|
||||
static inline u8 U(u8 r, u8 g, u8 b) { return static_cast<u8>(-0.14713f * r - 0.28886f * g + 0.436f * b); }
|
||||
static inline u8 V(u8 r, u8 g, u8 b) { return static_cast<u8>(0.615f * r - 0.51499f * g - 0.10001f * b); }
|
||||
YUV(const u8 rgb[3])
|
||||
{
|
||||
const u8 r = rgb[0];
|
||||
const u8 g = rgb[1];
|
||||
const u8 b = rgb[2];
|
||||
y = Y(r, g, b);
|
||||
u = U(r, g, b);
|
||||
v = V(r, g, b);
|
||||
}
|
||||
|
||||
static inline u8 Y(u8 r, u8 g, u8 b) { return static_cast<u8>(std::clamp(0.299f * r + 0.587f * g + 0.114f * b, 0.0f, 255.0f)); }
|
||||
static inline u8 U(u8 r, u8 g, u8 b) { return static_cast<u8>(std::clamp(-0.169f * r - 0.331f * g + 0.499f * b + 128, 0.0f, 255.0f)); }
|
||||
static inline u8 V(u8 r, u8 g, u8 b) { return static_cast<u8>(std::clamp(0.499f * r - 0.460f * g - 0.040f * b + 128, 0.0f, 255.0f)); }
|
||||
};
|
||||
|
||||
bool convert_image_format(CellCameraFormat input_format, CellGemVideoConvertFormatEnum output_format,
|
||||
const std::vector<u8>& video_data_in, u32 width, u32 height,
|
||||
u8* video_data_out, u32 video_data_out_size, std::string_view caller)
|
||||
template <bool use_gain>
|
||||
static inline void debayer_raw8_impl(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b)
|
||||
{
|
||||
if (output_format != CELL_GEM_NO_VIDEO_OUTPUT && !video_data_out)
|
||||
constexpr u32 in_pitch = 640;
|
||||
constexpr u32 out_pitch = 640 * 4;
|
||||
|
||||
for (u32 y = 0; y < 480 - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = src + y * in_pitch;
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst0 = dst + y * out_pitch;
|
||||
u8* dst1 = dst0 + out_pitch;
|
||||
|
||||
for (u32 x = 0; x < 640 - 1; x += 2, src0 += 2, src1 += 2, dst0 += 8, dst1 += 8)
|
||||
{
|
||||
u8 b = src0[0];
|
||||
u8 g0 = src0[1];
|
||||
u8 g1 = src1[0];
|
||||
u8 r = src1[1];
|
||||
|
||||
if constexpr (use_gain)
|
||||
{
|
||||
b = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
|
||||
g0 = static_cast<u8>(std::clamp(g0 * gain_g, 0.0f, 255.0f));
|
||||
g1 = static_cast<u8>(std::clamp(g1 * gain_g, 0.0f, 255.0f));
|
||||
r = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
|
||||
}
|
||||
|
||||
const u8 top[4] = { r, g0, b, alpha };
|
||||
const u8 bottom[4] = { r, g1, b, alpha };
|
||||
|
||||
// Top-Left
|
||||
std::memcpy(dst0, top, 4);
|
||||
|
||||
// Top-Right Pixel
|
||||
std::memcpy(dst0 + 4, top, 4);
|
||||
|
||||
// Bottom-Left Pixel
|
||||
std::memcpy(dst1, bottom, 4);
|
||||
|
||||
// Bottom-Right Pixel
|
||||
std::memcpy(dst1 + 4, bottom, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void debayer_raw8(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b)
|
||||
{
|
||||
if (gain_r != 1.0f || gain_g != 1.0f || gain_b != 1.0f)
|
||||
debayer_raw8_impl<true>(src, dst, alpha, gain_r, gain_g, gain_b);
|
||||
else
|
||||
debayer_raw8_impl<false>(src, dst, alpha, gain_r, gain_g, gain_b);
|
||||
}
|
||||
|
||||
bool convert_image_format(CellCameraFormat input_format, const CellGemVideoConvertAttribute& vc,
|
||||
const std::vector<u8>& video_data_in, u32 width, u32 height,
|
||||
u8* video_data_out, u32 video_data_out_size, u8* buffer_memory,
|
||||
std::string_view caller)
|
||||
{
|
||||
if (vc.output_format != CELL_GEM_NO_VIDEO_OUTPUT && !video_data_out)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 required_in_size = get_buffer_size_by_format(static_cast<s32>(input_format), width, height);
|
||||
const s32 required_out_size = cellGemGetVideoConvertSize(output_format);
|
||||
const s32 required_out_size = cellGemGetVideoConvertSize(vc.output_format);
|
||||
|
||||
if (video_data_in.size() != required_in_size)
|
||||
{
|
||||
@ -723,7 +789,7 @@ namespace gem
|
||||
|
||||
if (required_out_size < 0 || video_data_out_size != static_cast<u32>(required_out_size))
|
||||
{
|
||||
cellGem.error("convert: out_size unknown: required=%d, actual=%d, format %d (called from %s)", required_out_size, video_data_out_size, output_format, caller);
|
||||
cellGem.error("convert: out_size unknown: required=%d, actual=%d, format %d (called from %s)", required_out_size, video_data_out_size, vc.output_format, caller);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -732,7 +798,121 @@ namespace gem
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (output_format)
|
||||
thread_local std::vector<u8> corrected_buffer;
|
||||
thread_local std::vector<u8> combined_buffer;
|
||||
thread_local std::vector<u8> conversion_buffer;
|
||||
|
||||
const u8* src_data = video_data_in.data();
|
||||
const u8 alpha = vc.alpha;
|
||||
const f32 gain_r = vc.gain * vc.blue_gain;
|
||||
const f32 gain_g = vc.gain * vc.green_gain;
|
||||
const f32 gain_b = vc.gain * vc.red_gain;
|
||||
|
||||
// Only RAW8 should be relevant for cellGem unless I'm mistaken
|
||||
if (input_format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
// TODO: CELL_GEM_AUTO_WHITE_BALANCE
|
||||
// TODO: CELL_GEM_GAMMA_BOOST
|
||||
|
||||
// Correct outliers
|
||||
if (vc.conversion_flags & CELL_GEM_FILTER_OUTLIER_PIXELS)
|
||||
{
|
||||
corrected_buffer.resize(width * height);
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
const u8* src = src_data + y * 640;
|
||||
u8* dst = &corrected_buffer[y * 640];
|
||||
|
||||
for (u32 x = 0; x < width; x++, src++)
|
||||
{
|
||||
// Let's just say these 2 are outliers
|
||||
if (const u8 val = *src; val > 0 && val < 255)
|
||||
{
|
||||
*dst++ = val;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Just take the 4 neighbours for now
|
||||
s32 sum = 0;
|
||||
if (y >= 2) sum += *(src - (2 * 640));
|
||||
if (x >= 2) sum += *(src - 2);
|
||||
if (x < 638) sum += *(src + 2);
|
||||
if (y < 478) sum += *(src + (2 * 640));
|
||||
|
||||
*dst++ = sum / 4; // Ignore count. It will only be less than 4 on the edges
|
||||
}
|
||||
}
|
||||
|
||||
src_data = corrected_buffer.data();
|
||||
}
|
||||
|
||||
// Combine with previous frame
|
||||
if (buffer_memory && (vc.conversion_flags & CELL_GEM_COMBINE_PREVIOUS_INPUT_FRAME))
|
||||
{
|
||||
combined_buffer.resize(width * height);
|
||||
|
||||
for (u32 i = 0; i < combined_buffer.size(); i++)
|
||||
{
|
||||
const u8 val = src_data[i];
|
||||
u8& old = buffer_memory[i];
|
||||
combined_buffer[i] = (old + val) / 2;
|
||||
old = val;
|
||||
}
|
||||
|
||||
src_data = combined_buffer.data();
|
||||
}
|
||||
|
||||
switch (vc.output_format)
|
||||
{
|
||||
case CELL_GEM_YUV_640x480:
|
||||
case CELL_GEM_YUV422_640x480:
|
||||
case CELL_GEM_YUV411_640x480:
|
||||
{
|
||||
// Let's debayer the image first for YUV formats
|
||||
conversion_buffer.resize(cellGemGetVideoConvertSize(CELL_GEM_RGBA_640x480));
|
||||
|
||||
debayer_raw8(src_data, conversion_buffer.data(), alpha, gain_r, gain_g, gain_b);
|
||||
|
||||
src_data = conversion_buffer.data();
|
||||
input_format = CELL_CAMERA_RGBA;
|
||||
width = 640;
|
||||
height = 480;
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_BAYER_RESTORED:
|
||||
case CELL_GEM_BAYER_RESTORED_RGGB:
|
||||
case CELL_GEM_BAYER_RESTORED_RASTERIZED:
|
||||
{
|
||||
// Let's apply gain
|
||||
if (gain_r != 1.0f || gain_g != 1.0f || gain_b != 1.0f)
|
||||
{
|
||||
conversion_buffer.resize(cellGemGetVideoConvertSize(CELL_GEM_RGBA_640x480));
|
||||
|
||||
const f32 bggr_gains[2][2] = {{gain_b, gain_g}, {gain_g, gain_r}};
|
||||
const u8* src = src_data;
|
||||
u8* dst = conversion_buffer.data();
|
||||
|
||||
for (u32 y = 0; y < 480; y++)
|
||||
{
|
||||
const f32* gains = bggr_gains[y % 2];
|
||||
|
||||
for (u32 x = 0; x < 640; x++)
|
||||
{
|
||||
*dst++ = static_cast<u8>(std::clamp(*src++ * gains[x % 2], 0.0f, 255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
src_data = conversion_buffer.data();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (vc.output_format)
|
||||
{
|
||||
case CELL_GEM_RGBA_640x480: // RGBA output; 640*480*4-byte output buffer required
|
||||
{
|
||||
@ -740,51 +920,18 @@ namespace gem
|
||||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
const u32 out_pitch = width * 4;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst0 = video_data_out + y * out_pitch;
|
||||
u8* dst1 = dst0 + out_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst0 += 8, dst1 += 8)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
const u8 top[4] = { r, g0, b, 255 };
|
||||
const u8 bottom[4] = { r, g1, b, 255 };
|
||||
|
||||
// Top-Left
|
||||
std::memcpy(dst0, top, 4);
|
||||
|
||||
// Top-Right Pixel
|
||||
std::memcpy(dst0 + 4, top, 4);
|
||||
|
||||
// Bottom-Left Pixel
|
||||
std::memcpy(dst1, bottom, 4);
|
||||
|
||||
// Bottom-Right Pixel
|
||||
std::memcpy(dst1 + 4, bottom, 4);
|
||||
}
|
||||
}
|
||||
debayer_raw8(src_data, video_data_out, alpha, gain_r, gain_g, gain_b);
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
{
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -794,17 +941,18 @@ namespace gem
|
||||
{
|
||||
if (input_format == CELL_CAMERA_RAW8)
|
||||
{
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous)
|
||||
{
|
||||
// YUV 4:4:4 planar. 1 value each per pixel
|
||||
const u32 yuv_pitch = width;
|
||||
|
||||
u8* dst_y = video_data_out;
|
||||
@ -815,61 +963,21 @@ namespace gem
|
||||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_y0 = dst_y + y * yuv_pitch;
|
||||
u8* dst_y1 = dst_y0 + yuv_pitch;
|
||||
|
||||
u8* dst_u0 = dst_u + y * yuv_pitch;
|
||||
u8* dst_u1 = dst_u0 + yuv_pitch;
|
||||
|
||||
u8* dst_v0 = dst_v + y * yuv_pitch;
|
||||
u8* dst_v1 = dst_v0 + yuv_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst_y0 += 2, dst_y1 += 2, dst_u0 += 2, dst_u1 += 2, dst_v0 += 2, dst_v1 += 2)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_top = YUV(r, g0, b);
|
||||
const YUV yuv_bottom = YUV(r, g1, b);
|
||||
|
||||
dst_y0[0] = dst_y0[1] = yuv_top.y;
|
||||
dst_y1[0] = dst_y1[1] = yuv_bottom.y;
|
||||
|
||||
dst_u0[0] = dst_u0[1] = yuv_top.u;
|
||||
dst_u1[0] = dst_u1[1] = yuv_bottom.u;
|
||||
|
||||
dst_v0[0] = dst_v0[1] = yuv_top.v;
|
||||
dst_v1[0] = dst_v1[1] = yuv_bottom.v;
|
||||
}
|
||||
}
|
||||
fmt::throw_exception("Unreachable: should already be debayered");
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
{
|
||||
const u32 in_pitch = width / 4;
|
||||
const u32 in_pitch = width * 4;
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
const u8* src = &video_data_in[y * in_pitch];
|
||||
const u8* src = src_data + y * in_pitch;
|
||||
|
||||
for (u32 x = 0; x < width; x++, src += 4)
|
||||
{
|
||||
const u8 r = src[0];
|
||||
const u8 g = src[1];
|
||||
const u8 b = src[2];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv = YUV(r, g, b);
|
||||
const YUV yuv = YUV(src);
|
||||
|
||||
*dst_y++ = yuv.y;
|
||||
*dst_u++ = yuv.u;
|
||||
@ -880,8 +988,8 @@ namespace gem
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -889,6 +997,7 @@ namespace gem
|
||||
}
|
||||
case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous)
|
||||
{
|
||||
// YUV 4:2:2 planar. 1 Y value per pixel, 1 U/V value per 2 horizontal pixels
|
||||
const u32 y_pitch = width;
|
||||
const u32 uv_pitch = width / 2;
|
||||
|
||||
@ -900,43 +1009,7 @@ namespace gem
|
||||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_y0 = dst_y + y * y_pitch;
|
||||
u8* dst_y1 = dst_y0 + y_pitch;
|
||||
|
||||
u8* dst_u0 = dst_u + y * uv_pitch;
|
||||
u8* dst_u1 = dst_u0 + uv_pitch;
|
||||
|
||||
u8* dst_v0 = dst_v + y * uv_pitch;
|
||||
u8* dst_v1 = dst_v0 + uv_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst_y0 += 2, dst_y1 += 2)
|
||||
{
|
||||
const u8 b = src0[0];
|
||||
const u8 g0 = src0[1];
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_top = YUV(r, g0, b);
|
||||
const YUV yuv_bottom = YUV(r, g1, b);
|
||||
|
||||
dst_y0[0] = dst_y0[1] = yuv_top.y;
|
||||
dst_y1[0] = dst_y1[1] = yuv_bottom.y;
|
||||
|
||||
*dst_u0++ = yuv_top.u;
|
||||
*dst_u1++ = yuv_bottom.u;
|
||||
|
||||
*dst_v0++ = yuv_top.v;
|
||||
*dst_v1++ = yuv_bottom.v;
|
||||
}
|
||||
}
|
||||
fmt::throw_exception("Unreachable: should already be debayered");
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
@ -945,33 +1018,28 @@ namespace gem
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
const u8* src = &video_data_in[y * in_pitch];
|
||||
const u8* src = src_data + y * in_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 1; x += 2, src += 8, dst_y += 2)
|
||||
{
|
||||
const u8 r_0 = src[0];
|
||||
const u8 g_0 = src[1];
|
||||
const u8 b_0 = src[2];
|
||||
const u8 r_1 = src[4];
|
||||
const u8 g_1 = src[5];
|
||||
const u8 b_1 = src[6];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_0 = YUV(r_0, g_0, b_0);
|
||||
const u8 y_1 = YUV::Y(r_1, g_1, b_1);
|
||||
const YUV yuv_0 = YUV(src);
|
||||
const YUV yuv_1 = YUV(src + 4);
|
||||
|
||||
dst_y[0] = yuv_0.y;
|
||||
dst_y[1] = y_1;
|
||||
*dst_u++ = yuv_0.u;
|
||||
*dst_v++ = yuv_0.v;
|
||||
dst_y[1] = yuv_1.y;
|
||||
|
||||
// Average U/V from 2 horizontal pixels
|
||||
*dst_u++ = (yuv_0.u + yuv_1.u) / 2;
|
||||
*dst_v++ = (yuv_0.v + yuv_1.v) / 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -979,109 +1047,54 @@ namespace gem
|
||||
}
|
||||
case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous)
|
||||
{
|
||||
const u32 y_pitch = width;
|
||||
const u32 uv_pitch = width / 4;
|
||||
|
||||
// YUV 4:1:1 planar. 1 Y value per pixel, 1 U/V value per 2x2 pixel block
|
||||
u8* dst_y = video_data_out;
|
||||
u8* dst_u = dst_y + y_pitch * height;
|
||||
u8* dst_v = dst_u + uv_pitch * height;
|
||||
u8* dst_u = dst_y + 640 * 480;
|
||||
u8* dst_v = dst_u + 320 * 240;
|
||||
|
||||
switch (input_format)
|
||||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
{
|
||||
const u32 in_pitch = width;
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_y0 = dst_y + y * y_pitch;
|
||||
u8* dst_y1 = dst_y0 + y_pitch;
|
||||
|
||||
u8* dst_u0 = dst_u + y * uv_pitch;
|
||||
u8* dst_u1 = dst_u0 + uv_pitch;
|
||||
|
||||
u8* dst_v0 = dst_v + y * uv_pitch;
|
||||
u8* dst_v1 = dst_v0 + uv_pitch;
|
||||
|
||||
for (u32 x = 0; x < width - 3; x += 4, src0 += 4, src1 += 4, dst_y0 += 4, dst_y1 += 4)
|
||||
{
|
||||
const u8 b_left = src0[0];
|
||||
const u8 g0_left = src0[1];
|
||||
const u8 b_right = src0[2];
|
||||
const u8 g0_right = src0[3];
|
||||
|
||||
const u8 g1_left = src1[0];
|
||||
const u8 r_left = src1[1];
|
||||
const u8 g1_right = src1[2];
|
||||
const u8 r_right = src1[3];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_top_left = YUV(r_left, g0_left, b_left); // Re-used for top-right
|
||||
const u8 y_top_right = YUV::Y(r_right, g0_right, b_right);
|
||||
const YUV yuv_bottom_left = YUV(r_left, g1_left, b_left); // Re-used for bottom-right
|
||||
const u8 y_bottom_right = YUV::Y(r_right, g1_right, b_right);
|
||||
|
||||
dst_y0[0] = dst_y0[1] = yuv_top_left.y;
|
||||
dst_y0[2] = dst_y0[3] = y_top_right;
|
||||
|
||||
dst_y1[0] = dst_y1[1] = yuv_bottom_left.y;
|
||||
dst_y1[2] = dst_y1[3] = y_bottom_right;
|
||||
|
||||
*dst_u0++ = yuv_top_left.u;
|
||||
*dst_u1++ = yuv_bottom_left.u;
|
||||
|
||||
*dst_v0++ = yuv_top_left.v;
|
||||
*dst_v1++ = yuv_bottom_left.v;
|
||||
}
|
||||
}
|
||||
fmt::throw_exception("Unreachable: should already be debayered");
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RGBA:
|
||||
{
|
||||
const u32 in_pitch = width * 4;
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
// 2 rows at a time to get a 2x2 pixel block
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src = &video_data_in[y * in_pitch];
|
||||
const u8* src = src_data + y * in_pitch;
|
||||
const u8* src2 = src + in_pitch;
|
||||
u8* dst_y1 = dst_y + y * 640;
|
||||
u8* dst_y2 = dst_y1 + 640;
|
||||
|
||||
for (u32 x = 0; x < width - 3; x += 4, src += 16, dst_y += 4)
|
||||
for (u32 x = 0; x < width - 1; x += 2, src += 8, src2 += 8, dst_y1 += 2, dst_y2 += 2)
|
||||
{
|
||||
const u8 r_0 = src[0];
|
||||
const u8 g_0 = src[1];
|
||||
const u8 b_0 = src[2];
|
||||
const u8 r_1 = src[4];
|
||||
const u8 g_1 = src[5];
|
||||
const u8 b_1 = src[6];
|
||||
const u8 r_2 = src[8];
|
||||
const u8 g_2 = src[9];
|
||||
const u8 b_2 = src[10];
|
||||
const u8 r_3 = src[12];
|
||||
const u8 g_3 = src[13];
|
||||
const u8 b_3 = src[14];
|
||||
|
||||
// Convert RGBA to YUV
|
||||
const YUV yuv_0 = YUV(r_0, g_0, b_0);
|
||||
const u8 y_1 = YUV::Y(r_1, g_1, b_1);
|
||||
const u8 y_2 = YUV::Y(r_2, g_2, b_2);
|
||||
const u8 y_3 = YUV::Y(r_3, g_3, b_3);
|
||||
const YUV yuv_0 = YUV(src);
|
||||
const YUV yuv_1 = YUV(src + 4);
|
||||
const YUV yuv_2 = YUV(src2);
|
||||
const YUV yuv_3 = YUV(src2 + 4);
|
||||
|
||||
dst_y[0] = yuv_0.y;
|
||||
dst_y[1] = y_1;
|
||||
dst_y[2] = y_2;
|
||||
dst_y[3] = y_3;
|
||||
*dst_u++ = yuv_0.u;
|
||||
*dst_v++ = yuv_0.v;
|
||||
dst_y1[0] = yuv_0.y;
|
||||
dst_y1[1] = yuv_1.y;
|
||||
dst_y2[0] = yuv_2.y;
|
||||
dst_y2[1] = yuv_3.y;
|
||||
|
||||
// Average U/V from 2x2 pixel block
|
||||
*dst_u++ = (yuv_0.u + yuv_1.u + yuv_2.u + yuv_3.u) / 4;
|
||||
*dst_v++ = (yuv_0.v + yuv_1.v + yuv_2.v + yuv_3.v) / 4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1098,7 +1111,7 @@ namespace gem
|
||||
|
||||
for (u32 y = 0; y < height - 1; y += 2)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * in_pitch];
|
||||
const u8* src0 = src_data + y * in_pitch;
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst0 = video_data_out + (y / 2) * out_pitch;
|
||||
@ -1111,8 +1124,8 @@ namespace gem
|
||||
const u8 g1 = src1[0];
|
||||
const u8 r = src1[1];
|
||||
|
||||
const u8 top[4] = { r, g0, b, 255 };
|
||||
const u8 bottom[4] = { r, g1, b, 255 };
|
||||
const u8 top[4] = { r, g0, b, alpha };
|
||||
const u8 bottom[4] = { r, g1, b, alpha };
|
||||
|
||||
// Top-Left
|
||||
std::memcpy(dst0, top, 4);
|
||||
@ -1130,7 +1143,7 @@ namespace gem
|
||||
|
||||
for (u32 y = 0; y < height / 2; y++)
|
||||
{
|
||||
const u8* src = &video_data_in[y * 2 * in_pitch];
|
||||
const u8* src = src_data + y * 2 * in_pitch;
|
||||
u8* dst = video_data_out + y * out_pitch;
|
||||
|
||||
for (u32 x = 0; x < width / 2; x++, src += 4 * 2, dst += 4)
|
||||
@ -1142,8 +1155,8 @@ namespace gem
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1160,7 +1173,7 @@ namespace gem
|
||||
|
||||
for (u32 y = 0; y < dst_h; y++)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * 2 * in_pitch];
|
||||
const u8* src0 = src_data + y * 2 * in_pitch;
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst = video_data_out + y * out_pitch;
|
||||
@ -1181,8 +1194,8 @@ namespace gem
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@ -1204,7 +1217,7 @@ namespace gem
|
||||
|
||||
for (u32 y = 0; y < dst_h; y++)
|
||||
{
|
||||
const u8* src0 = &video_data_in[y * 2 * in_pitch];
|
||||
const u8* src0 = src_data + y * 2 * in_pitch;
|
||||
const u8* src1 = src0 + in_pitch;
|
||||
|
||||
u8* dst_r = dst_plane_r + y * out_pitch;
|
||||
@ -1228,8 +1241,8 @@ namespace gem
|
||||
}
|
||||
else
|
||||
{
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, output_format, caller);
|
||||
std::memcpy(video_data_out, video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
|
||||
cellGem.error("Unimplemented: Converting %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
std::memcpy(video_data_out, src_data, std::min<usz>(required_in_size, required_out_size));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@ -1241,7 +1254,7 @@ namespace gem
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellGem.error("Trying to convert %s to %s (called from %s)", input_format, output_format, caller);
|
||||
cellGem.error("Trying to convert %s to %s (called from %s)", input_format, vc.output_format, caller);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1420,13 +1433,15 @@ void gem_config_data::operator()()
|
||||
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
|
||||
if (gem::convert_image_format(shared_data.format, vc.output_format, video_data_in, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size, "cellGem"))
|
||||
if (gem::convert_image_format(shared_data.format, vc, video_data_in, shared_data.width, shared_data.height,
|
||||
vc.video_data_out ? vc.video_data_out.get_ptr() : nullptr, video_data_out_size,
|
||||
vc.buffer_memory ? vc.buffer_memory.get_ptr() : nullptr, "cellGem"))
|
||||
{
|
||||
cellGem.trace("Converted video frame of format %s to %s", shared_data.format.load(), vc.output_format.get());
|
||||
|
||||
if (g_cfg.io.paint_move_spheres)
|
||||
{
|
||||
paint_spheres(vc.output_format, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size);
|
||||
paint_spheres(vc.output_format, shared_data.width, shared_data.height, vc.video_data_out ? vc.video_data_out.get_ptr() : nullptr, video_data_out_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,14 +13,21 @@ LOG_CHANNEL(ps_move);
|
||||
|
||||
namespace gem
|
||||
{
|
||||
extern bool convert_image_format(CellCameraFormat input_format, CellGemVideoConvertFormatEnum output_format,
|
||||
extern bool convert_image_format(CellCameraFormat input_format, const CellGemVideoConvertAttribute& vc,
|
||||
const std::vector<u8>& video_data_in, u32 width, u32 height,
|
||||
u8* video_data_out, u32 video_data_out_size, std::string_view caller);
|
||||
u8* video_data_out, u32 video_data_out_size, u8* buffer_memory,
|
||||
std::string_view caller);
|
||||
}
|
||||
|
||||
template <bool DiagnosticsEnabled>
|
||||
ps_move_tracker<DiagnosticsEnabled>::ps_move_tracker()
|
||||
{
|
||||
m_vc_attr.alpha = 255;
|
||||
m_vc_attr.gain = 1.0f;
|
||||
m_vc_attr.red_gain = 1.0f;
|
||||
m_vc_attr.green_gain = 1.0f;
|
||||
m_vc_attr.blue_gain = 1.0f;
|
||||
|
||||
init_workers();
|
||||
}
|
||||
|
||||
@ -238,7 +245,9 @@ void ps_move_tracker<DiagnosticsEnabled>::convert_image(s32 output_format)
|
||||
m_image_binary[index].resize(size);
|
||||
}
|
||||
|
||||
if (gem::convert_image_format(CellCameraFormat{m_format}, CellGemVideoConvertFormatEnum{output_format}, m_image_data, width, height, m_image_rgba.data(), ::size32(m_image_rgba), "gemTracker"))
|
||||
m_vc_attr.output_format = CellGemVideoConvertFormatEnum{output_format};
|
||||
|
||||
if (gem::convert_image_format(CellCameraFormat{m_format}, m_vc_attr, m_image_data, width, height, m_image_rgba.data(), ::size32(m_image_rgba), nullptr, "gemTracker"))
|
||||
{
|
||||
ps_move.trace("Converted video frame of format %s to %s", CellCameraFormat{m_format}, CellGemVideoConvertFormatEnum{output_format});
|
||||
}
|
||||
|
||||
@ -77,6 +77,8 @@ private:
|
||||
|
||||
void draw_sphere_size_range(f32 result_radius);
|
||||
|
||||
CellGemVideoConvertAttribute m_vc_attr {};
|
||||
|
||||
u32 m_width = 0;
|
||||
u32 m_height = 0;
|
||||
s32 m_format = 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user