mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
TAV: more various fixes and confirming temporal level 3 is unsuitable
This commit is contained in:
@@ -5,4 +5,5 @@ set PATH=\tvdos\installer;\tvdos\tuidev;$PATH
|
||||
set KEYBOARD=us_colemak
|
||||
|
||||
rem this line specifies which shell to be presented after the boot precess:
|
||||
zfm
|
||||
command -fancy
|
||||
|
||||
@@ -18,6 +18,7 @@ const SND_BASE_ADDR = audio.getBaseAddr()
|
||||
const SND_MEM_ADDR = audio.getMemAddr()
|
||||
const pcm = require("pcm")
|
||||
const MP2_FRAME_SIZE = [144,216,252,288,360,432,504,576,720,864,1008,1152,1440,1728]
|
||||
const TAV_TEMPORAL_LEVELS = 2
|
||||
|
||||
// Tile encoding modes (same as TEV block modes)
|
||||
const TAV_MODE_SKIP = 0x00
|
||||
@@ -1108,7 +1109,7 @@ try {
|
||||
header.qualityLevel,
|
||||
QLUT[header.qualityY], QLUT[header.qualityCo], QLUT[header.qualityCg],
|
||||
header.channelLayout,
|
||||
header.waveletFilter, header.decompLevels, 2,
|
||||
header.waveletFilter, header.decompLevels, TAV_TEMPORAL_LEVELS,
|
||||
header.entropyCoder,
|
||||
bufferOffset
|
||||
)
|
||||
@@ -1181,7 +1182,7 @@ try {
|
||||
header.qualityLevel,
|
||||
QLUT[header.qualityY], QLUT[header.qualityCo], QLUT[header.qualityCg],
|
||||
header.channelLayout,
|
||||
header.waveletFilter, header.decompLevels, 2,
|
||||
header.waveletFilter, header.decompLevels, TAV_TEMPORAL_LEVELS,
|
||||
header.entropyCoder,
|
||||
nextOffset
|
||||
)
|
||||
@@ -1223,7 +1224,7 @@ try {
|
||||
header.qualityLevel,
|
||||
QLUT[header.qualityY], QLUT[header.qualityCo], QLUT[header.qualityCg],
|
||||
header.channelLayout,
|
||||
header.waveletFilter, header.decompLevels, 2,
|
||||
header.waveletFilter, header.decompLevels, TAV_TEMPORAL_LEVELS,
|
||||
header.entropyCoder,
|
||||
decodingOffset
|
||||
)
|
||||
@@ -1526,7 +1527,7 @@ try {
|
||||
header.qualityLevel,
|
||||
QLUT[header.qualityY], QLUT[header.qualityCo], QLUT[header.qualityCg],
|
||||
header.channelLayout,
|
||||
header.waveletFilter, header.decompLevels, 2,
|
||||
header.waveletFilter, header.decompLevels, TAV_TEMPORAL_LEVELS,
|
||||
header.entropyCoder,
|
||||
readyGopData.slot * SLOT_SIZE
|
||||
)
|
||||
@@ -1674,7 +1675,7 @@ try {
|
||||
header.qualityLevel,
|
||||
QLUT[header.qualityY], QLUT[header.qualityCo], QLUT[header.qualityCg],
|
||||
header.channelLayout,
|
||||
header.waveletFilter, header.decompLevels, 2,
|
||||
header.waveletFilter, header.decompLevels, TAV_TEMPORAL_LEVELS,
|
||||
header.entropyCoder,
|
||||
decodingGopData.slot * SLOT_SIZE
|
||||
)
|
||||
@@ -1714,7 +1715,7 @@ try {
|
||||
header.qualityLevel,
|
||||
QLUT[header.qualityY], QLUT[header.qualityCo], QLUT[header.qualityCg],
|
||||
header.channelLayout,
|
||||
header.waveletFilter, header.decompLevels, 2,
|
||||
header.waveletFilter, header.decompLevels, TAV_TEMPORAL_LEVELS,
|
||||
header.entropyCoder,
|
||||
readyGopData.slot * SLOT_SIZE
|
||||
)
|
||||
@@ -1791,7 +1792,7 @@ try {
|
||||
header.qualityLevel,
|
||||
QLUT[header.qualityY], QLUT[header.qualityCo], QLUT[header.qualityCg],
|
||||
header.channelLayout,
|
||||
header.waveletFilter, header.decompLevels, 2,
|
||||
header.waveletFilter, header.decompLevels, TAV_TEMPORAL_LEVELS,
|
||||
header.entropyCoder,
|
||||
targetOffset
|
||||
)
|
||||
|
||||
@@ -4960,8 +4960,7 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
private fun getTemporalQuantizerScale(temporalLevel: Int): Float {
|
||||
val BETA = 0.6f // Temporal scaling exponent (aggressive for temporal high-pass)
|
||||
val KAPPA = 1.14f
|
||||
val TEMPORAL_BASE_SCALE = 1.0f // Don't reduce tLL quantization (same as intra)
|
||||
return TEMPORAL_BASE_SCALE * 2.0f.pow(BETA * temporalLevel.toFloat().pow(KAPPA))
|
||||
return 2.0f.pow(BETA * temporalLevel.toFloat().pow(KAPPA))
|
||||
}
|
||||
|
||||
// level is one-based index
|
||||
@@ -6224,9 +6223,20 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
val tempRow = FloatArray(maxSize)
|
||||
val tempCol = FloatArray(maxSize)
|
||||
|
||||
// Pre-calculate all intermediate widths and heights (same fix as TAD/temporal/spatial-forward)
|
||||
// This ensures correct reconstruction for non-power-of-2 dimensions
|
||||
val widths = IntArray(levels + 1)
|
||||
val heights = IntArray(levels + 1)
|
||||
widths[0] = width
|
||||
heights[0] = height
|
||||
for (i in 1..levels) {
|
||||
widths[i] = (widths[i - 1] + 1) / 2
|
||||
heights[i] = (heights[i - 1] + 1) / 2
|
||||
}
|
||||
|
||||
for (level in levels - 1 downTo 0) {
|
||||
val currentWidth = width shr level
|
||||
val currentHeight = height shr level
|
||||
val currentWidth = widths[level]
|
||||
val currentHeight = heights[level]
|
||||
|
||||
// Handle edge cases for very small decomposition levels
|
||||
if (currentWidth < 1 || currentHeight < 1) continue // Skip invalid sizes
|
||||
@@ -7115,6 +7125,14 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
// Only needed for GOPs with multiple frames (skip for I-frames)
|
||||
if (numFrames < 2) return
|
||||
|
||||
// Pre-calculate all intermediate lengths for temporal DWT (same fix as TAD)
|
||||
// This ensures correct reconstruction for non-power-of-2 GOP sizes
|
||||
val temporalLengths = IntArray(temporalLevels + 1)
|
||||
temporalLengths[0] = numFrames
|
||||
for (i in 1..temporalLevels) {
|
||||
temporalLengths[i] = (temporalLengths[i - 1] + 1) / 2
|
||||
}
|
||||
|
||||
val temporalLine = FloatArray(numFrames)
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
@@ -7125,9 +7143,9 @@ class GraphicsJSR223Delegate(private val vm: VM) {
|
||||
temporalLine[t] = gopData[t][pixelIdx]
|
||||
}
|
||||
|
||||
// Apply inverse temporal DWT with multiple levels (reverse order)
|
||||
// Apply inverse temporal DWT with multiple levels using pre-calculated lengths (reverse order)
|
||||
for (level in temporalLevels - 1 downTo 0) {
|
||||
val levelFrames = numFrames shr level
|
||||
val levelFrames = temporalLengths[level]
|
||||
if (levelFrames >= 2) {
|
||||
tavApplyTemporalDWTInverse1D(temporalLine, levelFrames)
|
||||
}
|
||||
|
||||
@@ -124,6 +124,13 @@ static int needs_alpha_channel(int channel_layout) {
|
||||
return channel_layouts[channel_layout].has_alpha;
|
||||
}
|
||||
|
||||
// Coefficient preprocessing modes
|
||||
typedef enum {
|
||||
PREPROCESS_TWOBITMAP = 0, // Twobit-plane significance map (default, best compression)
|
||||
PREPROCESS_EZBC = 1, // EZBC embedded zero block coding
|
||||
PREPROCESS_RAW = 2 // No preprocessing - raw coefficients
|
||||
} preprocess_mode_t;
|
||||
|
||||
// Default settings
|
||||
#define DEFAULT_WIDTH 560
|
||||
#define DEFAULT_HEIGHT 448
|
||||
@@ -133,7 +140,7 @@ static int needs_alpha_channel(int channel_layout) {
|
||||
#define DEFAULT_PCM_ZSTD_LEVEL 3
|
||||
#define TEMPORAL_GOP_SIZE 24
|
||||
#define TEMPORAL_GOP_SIZE_MIN 10 // Minimum GOP size to avoid decoder hiccups
|
||||
#define TEMPORAL_DECOMP_LEVEL 2
|
||||
#define TEMPORAL_DECOMP_LEVEL 2 // 3 levels make too much afterimages and nonmoving pixels
|
||||
|
||||
// Single-pass scene change detection constants
|
||||
#define SCENE_CHANGE_THRESHOLD_SOFT 0.72
|
||||
@@ -1804,7 +1811,7 @@ typedef struct tav_encoder_s {
|
||||
int intra_only; // Force all tiles to use INTRA mode (disable delta encoding)
|
||||
int monoblock; // Single DWT tile mode (encode entire frame as one tile)
|
||||
int perceptual_tuning; // 1 = perceptual quantisation (default), 0 = uniform quantisation
|
||||
int enable_ezbc; // 1 = use EZBC (Embedded Zero Block Coding) for significance maps, 0 = use twobit-map (default)
|
||||
preprocess_mode_t preprocess_mode; // Coefficient preprocessing mode (TWOBITMAP=default, EZBC, RAW)
|
||||
int channel_layout; // Channel layout: 0=Y-Co-Cg, 1=Y-only, 2=Y-Co-Cg-A, 3=Y-A, 4=Co-Cg
|
||||
int progressive_mode; // 0 = interlaced (default), 1 = progressive
|
||||
int grain_synthesis; // 1 = enable grain synthesis (default), 0 = disable
|
||||
@@ -2302,10 +2309,10 @@ static void quantise_dwt_coefficients_perceptual_per_coeff_no_normalisation(tav_
|
||||
float *coeffs, int16_t *quantised, int size,
|
||||
int base_quantiser, int width, int height,
|
||||
int decomp_levels, int is_chroma, int frame_count);
|
||||
static size_t preprocess_coefficients_variable_layout(int enable_ezbc, int width, int height,
|
||||
static size_t preprocess_coefficients_variable_layout(preprocess_mode_t preprocess_mode, int width, int height,
|
||||
int16_t *coeffs_y, int16_t *coeffs_co, int16_t *coeffs_cg, int16_t *coeffs_alpha,
|
||||
int coeff_count, int channel_layout, uint8_t *output_buffer);
|
||||
static size_t preprocess_gop_unified(int enable_ezbc, int16_t **quant_y, int16_t **quant_co, int16_t **quant_cg,
|
||||
static size_t preprocess_gop_unified(preprocess_mode_t preprocess_mode, int16_t **quant_y, int16_t **quant_co, int16_t **quant_cg,
|
||||
int num_frames, int num_pixels, int width, int height, int channel_layout,
|
||||
uint8_t *output_buffer);
|
||||
|
||||
@@ -2357,6 +2364,7 @@ static void show_usage(const char *program_name) {
|
||||
printf(" --single-pass Disable two-pass encoding with wavelet-based scene change detection (optimal GOP boundaries)\n");
|
||||
printf(" --mc-ezbc Enable MC-EZBC block-based motion compensation (requires --temporal-dwt, implies --ezbc)\n");
|
||||
printf(" --ezbc Enable EZBC (Embedded Zero Block Coding) entropy coding\n");
|
||||
printf(" --raw-coeffs Use raw coefficients (no significance map preprocessing, for testing)\n");
|
||||
printf(" --ictcp Use ICtCp colour space instead of YCoCg-R (use when source is in BT.2100)\n");
|
||||
printf(" --no-perceptual-tuning Disable perceptual quantisation\n");
|
||||
printf(" --no-dead-zone Disable dead-zone quantisation (for comparison/testing)\n");
|
||||
@@ -2423,7 +2431,7 @@ static tav_encoder_t* create_encoder(void) {
|
||||
enc->intra_only = 0;
|
||||
enc->monoblock = 1; // Default to monoblock mode
|
||||
enc->perceptual_tuning = 1; // Default to perceptual quantisation (versions 5/6)
|
||||
enc->enable_ezbc = 0; // default to twobit-map as EZBC+Zstd 3 = Twobitmap+Zstd 15, and Twobitmap is faster to decode
|
||||
enc->preprocess_mode = PREPROCESS_TWOBITMAP; // default to twobit-map as EZBC+Zstd 3 = Twobitmap+Zstd 15, and Twobitmap is faster to decode
|
||||
enc->channel_layout = CHANNEL_LAYOUT_YCOCG; // Default to Y-Co-Cg
|
||||
enc->audio_bitrate = 0; // 0 = use quality table
|
||||
enc->encode_limit = 0; // Default: no frame limit
|
||||
@@ -2438,9 +2446,9 @@ static tav_encoder_t* create_encoder(void) {
|
||||
|
||||
// GOP / temporal DWT settings
|
||||
enc->enable_temporal_dwt = 1; // Mutually exclusive with use_delta_encoding
|
||||
enc->temporal_gop_capacity = TEMPORAL_GOP_SIZE; // 16 frames
|
||||
enc->temporal_gop_capacity = TEMPORAL_GOP_SIZE; // 24 frames
|
||||
enc->temporal_gop_frame_count = 0;
|
||||
enc->temporal_decomp_levels = TEMPORAL_DECOMP_LEVEL; // 2 levels of temporal DWT (16 -> 4x4 subbands)
|
||||
enc->temporal_decomp_levels = TEMPORAL_DECOMP_LEVEL; // 3 levels of temporal DWT (24 -> 12 -> 6 -> 3 temporal subbands)
|
||||
enc->temporal_gop_rgb_frames = NULL;
|
||||
enc->temporal_gop_y_frames = NULL;
|
||||
enc->temporal_gop_co_frames = NULL;
|
||||
@@ -3130,33 +3138,35 @@ static void dwt_53_inverse_1d(float *data, int length) {
|
||||
// Temporal Subband Quantization
|
||||
// =============================================================================
|
||||
|
||||
// Determine temporal subband level for a frame index after multi-level temporal DWT
|
||||
// With 2 decomposition levels on 16 frames:
|
||||
// - Level 0 (tLL): frames 0-3 (4 frames, low-pass)
|
||||
// - Level 1 (tLH, tHL, tHH of level 1): frames 4-7, 8-11, 12-15 (12 frames, high-pass level 1)
|
||||
// - Level 2 would be: frames in the high-pass of the high-pass (if we had 3 levels)
|
||||
// Determine which temporal decomposition level a frame belongs to after 3D DWT
|
||||
// With 3 decomposition levels on 24 frames:
|
||||
// - Level 0 (tLLL): frames 0-2 (3 frames, coarsest low-pass)
|
||||
// - Level 1 (tLLH): frames 3-5 (3 frames, high-pass from 3rd decomposition)
|
||||
// - Level 2 (tLH): frames 6-11 (6 frames, high-pass from 2nd decomposition)
|
||||
// - Level 3 (tH): frames 12-23 (12 frames, high-pass from 1st decomposition)
|
||||
static int get_temporal_subband_level(int frame_idx, int num_frames, int temporal_levels) {
|
||||
// After temporal DWT with 2 levels:
|
||||
// Frames 0...num_frames/(2^2) = tLL (temporal low-low, coarsest)
|
||||
// Remaining frames are temporal high-pass subbands
|
||||
// After temporal DWT with N levels, frames are organized as:
|
||||
// Frames 0...num_frames/(2^N) = tL...L (N low-passes, coarsest)
|
||||
// Remaining frames are temporal high-pass subbands at various levels
|
||||
|
||||
int frames_per_level0 = num_frames >> temporal_levels; // 16 >> 2 = 4
|
||||
|
||||
if (frame_idx < frames_per_level0) {
|
||||
return 0; // Coarsest temporal level (tLL)
|
||||
} else if (frame_idx < (num_frames >> 1)) {
|
||||
return 1; // First level high-pass (tLH, tHL, tHH from level 1)
|
||||
} else {
|
||||
return 2; // Finest level high-pass
|
||||
// Check each level boundary from coarsest to finest
|
||||
for (int level = 0; level < temporal_levels; level++) {
|
||||
int frames_at_this_level = num_frames >> (temporal_levels - level);
|
||||
if (frame_idx < frames_at_this_level) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
// Finest level (first decomposition's high-pass)
|
||||
return temporal_levels;
|
||||
}
|
||||
|
||||
// Quantize 3D DWT coefficients with SEPARABLE temporal-spatial quantization
|
||||
//
|
||||
// IMPORTANT: This implements a separable quantization approach (temporal × spatial)
|
||||
// After dwt_3d_forward(), the GOP coefficients have this structure:
|
||||
// - Temporal DWT applied first (16 frames → 2 levels)
|
||||
// → Results in temporal subbands: tLL (frames 0-3), tLH (4-7), tHL (8-11), tHH (12-15)
|
||||
// - Temporal DWT applied first (24 frames → 3 levels)
|
||||
// → Results in temporal subbands: tLLL (frames 0-2), tLLH (3-5), tLH (6-11), tH (12-23)
|
||||
// - Then spatial DWT applied to each temporal subband
|
||||
// → Each frame now contains 2D spatial coefficients (LL, LH, HL, HH subbands)
|
||||
//
|
||||
@@ -3177,7 +3187,6 @@ static void quantise_3d_dwt_coefficients(tav_encoder_t *enc,
|
||||
int is_chroma) {
|
||||
const float BETA = 0.6f; // Temporal scaling exponent (aggressive for temporal high-pass)
|
||||
const float KAPPA = 1.14f;
|
||||
const float TEMPORAL_BASE_SCALE = 1.0f; // Don't reduce tLL quantization (same as intra)
|
||||
|
||||
// Process each temporal subband independently (separable approach)
|
||||
for (int t = 0; t < num_frames; t++) {
|
||||
@@ -3193,7 +3202,7 @@ static void quantise_3d_dwt_coefficients(tav_encoder_t *enc,
|
||||
// - Level 0 (tLL): 16 * 1.0 * 2^0 = 16 (same as intra-only)
|
||||
// - Level 1 (tH): 16 * 1.0 * 2^2.0 = 64 (4× base, aggressive)
|
||||
// - Level 2 (tHH): 16 * 1.0 * 2^4.0 = 256 → clamped to 255 (very aggressive)
|
||||
float temporal_scale = TEMPORAL_BASE_SCALE * powf(2.0f, BETA * powf(temporal_level, KAPPA));
|
||||
float temporal_scale = powf(2.0f, BETA * powf(temporal_level, KAPPA));
|
||||
float temporal_quantiser = base_quantiser * temporal_scale;
|
||||
|
||||
// Convert to integer for quantization
|
||||
@@ -3208,8 +3217,8 @@ static void quantise_3d_dwt_coefficients(tav_encoder_t *enc,
|
||||
//
|
||||
// CRITICAL: Use no_normalisation variant when EZBC is enabled
|
||||
// - EZBC mode: coefficients must be denormalized (quantize + multiply back)
|
||||
// - Twobit-map mode: coefficients stay normalized (quantize only)
|
||||
if (enc->enable_ezbc) {
|
||||
// - Twobit-map/raw mode: coefficients stay normalized (quantize only)
|
||||
if (enc->preprocess_mode == PREPROCESS_EZBC) {
|
||||
quantise_dwt_coefficients_perceptual_per_coeff_no_normalisation(
|
||||
enc,
|
||||
gop_coeffs[t], // Input: spatial coefficients for this temporal subband
|
||||
@@ -4148,7 +4157,7 @@ static size_t encode_pframe_residual(tav_encoder_t *enc, int qY) {
|
||||
int16_t *quantised_co = enc->reusable_quantised_co;
|
||||
int16_t *quantised_cg = enc->reusable_quantised_cg;
|
||||
|
||||
if (enc->enable_ezbc) {
|
||||
if (enc->preprocess_mode == PREPROCESS_EZBC) {
|
||||
// EZBC mode: Quantize with perceptual weighting but no normalization (division by quantizer)
|
||||
// EZBC will compress by encoding only significant bitplanes
|
||||
// fprintf(stderr, "[EZBC-QUANT-PFRAME] Using perceptual quantization without normalization\n");
|
||||
@@ -4186,7 +4195,7 @@ static size_t encode_pframe_residual(tav_encoder_t *enc, int qY) {
|
||||
// Step 6: Preprocess coefficients (significance map compression)
|
||||
int total_coeffs = frame_size * 3; // Y + Co + Cg
|
||||
uint8_t *preprocessed = malloc(total_coeffs * sizeof(int16_t) + 1024); // Extra space for map
|
||||
size_t preprocessed_size = preprocess_coefficients_variable_layout(enc->enable_ezbc, enc->width, enc->height,
|
||||
size_t preprocessed_size = preprocess_coefficients_variable_layout(enc->preprocess_mode, enc->width, enc->height,
|
||||
quantised_y, quantised_co, quantised_cg,
|
||||
NULL, frame_size, enc->channel_layout,
|
||||
preprocessed);
|
||||
@@ -4480,7 +4489,7 @@ static size_t encode_pframe_adaptive(tav_encoder_t *enc, int qY) {
|
||||
// Step 8: Preprocess coefficients
|
||||
int total_coeffs = frame_size * 3;
|
||||
uint8_t *preprocessed = malloc(total_coeffs * sizeof(int16_t) + 1024);
|
||||
size_t preprocessed_size = preprocess_coefficients_variable_layout(enc->enable_ezbc, enc->width, enc->height,
|
||||
size_t preprocessed_size = preprocess_coefficients_variable_layout(enc->preprocess_mode, enc->width, enc->height,
|
||||
quantised_y, quantised_co, quantised_cg,
|
||||
NULL, frame_size, enc->channel_layout,
|
||||
preprocessed);
|
||||
@@ -4713,7 +4722,7 @@ static size_t encode_bframe_adaptive(tav_encoder_t *enc, int qY) {
|
||||
// Step 8: Preprocess coefficients
|
||||
int total_coeffs = frame_size * 3;
|
||||
uint8_t *preprocessed = malloc(total_coeffs * sizeof(int16_t) + 1024);
|
||||
size_t preprocessed_size = preprocess_coefficients_variable_layout(enc->enable_ezbc, enc->width, enc->height,
|
||||
size_t preprocessed_size = preprocess_coefficients_variable_layout(enc->preprocess_mode, enc->width, enc->height,
|
||||
quantised_y, quantised_co, quantised_cg,
|
||||
NULL, frame_size, enc->channel_layout,
|
||||
preprocessed);
|
||||
@@ -5382,7 +5391,7 @@ static size_t gop_flush(tav_encoder_t *enc, FILE *output, int base_quantiser,
|
||||
uint8_t *preprocessed_buffer = malloc(max_preprocessed_size);
|
||||
|
||||
size_t preprocessed_size = preprocess_gop_unified(
|
||||
enc->enable_ezbc, quant_y, quant_co, quant_cg,
|
||||
enc->preprocess_mode, quant_y, quant_co, quant_cg,
|
||||
actual_gop_size, num_pixels, enc->width, enc->height, enc->channel_layout,
|
||||
preprocessed_buffer);
|
||||
|
||||
@@ -6023,6 +6032,14 @@ static void dwt_3d_forward(float **gop_data, int width, int height, int num_fram
|
||||
int num_pixels = width * height;
|
||||
float *temporal_line = malloc(num_frames * sizeof(float));
|
||||
|
||||
// Pre-calculate all intermediate lengths for temporal DWT (same fix as TAD)
|
||||
// This ensures correct reconstruction for non-power-of-2 GOP sizes
|
||||
int *temporal_lengths = malloc((temporal_levels + 1) * sizeof(int));
|
||||
temporal_lengths[0] = num_frames;
|
||||
for (int i = 1; i <= temporal_levels; i++) {
|
||||
temporal_lengths[i] = (temporal_lengths[i - 1] + 1) / 2;
|
||||
}
|
||||
|
||||
// Step 1: Apply temporal DWT to each spatial location across all GOP frames
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
@@ -6033,9 +6050,9 @@ static void dwt_3d_forward(float **gop_data, int width, int height, int num_fram
|
||||
temporal_line[t] = gop_data[t][pixel_idx];
|
||||
}
|
||||
|
||||
// Apply temporal DWT with multiple levels
|
||||
// Apply temporal DWT with multiple levels using pre-calculated lengths
|
||||
for (int level = 0; level < temporal_levels; level++) {
|
||||
int level_frames = num_frames >> level;
|
||||
int level_frames = temporal_lengths[level];
|
||||
if (level_frames >= 2) {
|
||||
// dwt_temporal_1d_forward_53(temporal_line, level_frames); // CDF 5/3 worse for motion-compensated frames
|
||||
dwt_haar_forward_1d(temporal_line, level_frames); // Haar better for imperfect alignment
|
||||
@@ -6049,6 +6066,7 @@ static void dwt_3d_forward(float **gop_data, int width, int height, int num_fram
|
||||
}
|
||||
}
|
||||
|
||||
free(temporal_lengths);
|
||||
free(temporal_line);
|
||||
|
||||
// Step 2: Apply 2D spatial DWT to each temporal subband (each frame after temporal DWT)
|
||||
@@ -6080,6 +6098,14 @@ static void dwt_3d_inverse(float **gop_data, int width, int height, int num_fram
|
||||
int num_pixels = width * height;
|
||||
float *temporal_line = malloc(num_frames * sizeof(float));
|
||||
|
||||
// Pre-calculate all intermediate lengths for temporal DWT (same fix as TAD)
|
||||
// This ensures correct reconstruction for non-power-of-2 GOP sizes
|
||||
int *temporal_lengths = malloc((temporal_levels + 1) * sizeof(int));
|
||||
temporal_lengths[0] = num_frames;
|
||||
for (int i = 1; i <= temporal_levels; i++) {
|
||||
temporal_lengths[i] = (temporal_lengths[i - 1] + 1) / 2;
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int pixel_idx = y * width + x;
|
||||
@@ -6089,9 +6115,9 @@ static void dwt_3d_inverse(float **gop_data, int width, int height, int num_fram
|
||||
temporal_line[t] = gop_data[t][pixel_idx];
|
||||
}
|
||||
|
||||
// Apply inverse temporal DWT with multiple levels (reverse order)
|
||||
// Apply inverse temporal DWT with multiple levels using pre-calculated lengths (reverse order)
|
||||
for (int level = temporal_levels - 1; level >= 0; level--) {
|
||||
int level_frames = num_frames >> level;
|
||||
int level_frames = temporal_lengths[level];
|
||||
if (level_frames >= 2) {
|
||||
dwt_temporal_1d_inverse_53(temporal_line, level_frames);
|
||||
}
|
||||
@@ -6104,6 +6130,7 @@ static void dwt_3d_inverse(float **gop_data, int width, int height, int num_fram
|
||||
}
|
||||
}
|
||||
|
||||
free(temporal_lengths);
|
||||
free(temporal_line);
|
||||
}
|
||||
|
||||
@@ -6328,9 +6355,20 @@ static void dwt_2d_forward_flexible(float *tile_data, int width, int height, int
|
||||
float *temp_row = malloc(max_size * sizeof(float));
|
||||
float *temp_col = malloc(max_size * sizeof(float));
|
||||
|
||||
// Pre-calculate all intermediate widths and heights (same fix as TAD/temporal)
|
||||
// This ensures correct reconstruction for non-power-of-2 dimensions
|
||||
int *widths = malloc((levels + 1) * sizeof(int));
|
||||
int *heights = malloc((levels + 1) * sizeof(int));
|
||||
widths[0] = width;
|
||||
heights[0] = height;
|
||||
for (int i = 1; i <= levels; i++) {
|
||||
widths[i] = (widths[i - 1] + 1) / 2;
|
||||
heights[i] = (heights[i - 1] + 1) / 2;
|
||||
}
|
||||
|
||||
for (int level = 0; level < levels; level++) {
|
||||
int current_width = width >> level;
|
||||
int current_height = height >> level;
|
||||
int current_width = widths[level];
|
||||
int current_height = heights[level];
|
||||
if (current_width < 1 || current_height < 1) break;
|
||||
|
||||
// Row transform (horizontal)
|
||||
@@ -6380,6 +6418,8 @@ static void dwt_2d_forward_flexible(float *tile_data, int width, int height, int
|
||||
}
|
||||
}
|
||||
|
||||
free(widths);
|
||||
free(heights);
|
||||
free(temp_row);
|
||||
free(temp_col);
|
||||
}
|
||||
@@ -6391,10 +6431,21 @@ static void dwt_2d_haar_inverse_flexible(float *tile_data, int width, int height
|
||||
float *temp_row = malloc(max_size * sizeof(float));
|
||||
float *temp_col = malloc(max_size * sizeof(float));
|
||||
|
||||
// Pre-calculate all intermediate widths and heights (same fix as TAD/temporal/forward)
|
||||
// This ensures correct reconstruction for non-power-of-2 dimensions
|
||||
int *widths = malloc((levels + 1) * sizeof(int));
|
||||
int *heights = malloc((levels + 1) * sizeof(int));
|
||||
widths[0] = width;
|
||||
heights[0] = height;
|
||||
for (int i = 1; i <= levels; i++) {
|
||||
widths[i] = (widths[i - 1] + 1) / 2;
|
||||
heights[i] = (heights[i - 1] + 1) / 2;
|
||||
}
|
||||
|
||||
// Apply inverse transform in reverse order of levels
|
||||
for (int level = levels - 1; level >= 0; level--) {
|
||||
int current_width = width >> level;
|
||||
int current_height = height >> level;
|
||||
int current_width = widths[level];
|
||||
int current_height = heights[level];
|
||||
if (current_width < 1 || current_height < 1) continue;
|
||||
|
||||
// Column inverse transform (vertical) - done first to reverse forward order
|
||||
@@ -6424,6 +6475,8 @@ static void dwt_2d_haar_inverse_flexible(float *tile_data, int width, int height
|
||||
}
|
||||
}
|
||||
|
||||
free(widths);
|
||||
free(heights);
|
||||
free(temp_row);
|
||||
free(temp_col);
|
||||
}
|
||||
@@ -6519,6 +6572,33 @@ static size_t preprocess_coefficients_twobitmap(int16_t *coeffs_y, int16_t *coef
|
||||
return map_bytes * total_maps + total_others * sizeof(int16_t);
|
||||
}
|
||||
|
||||
// Raw preprocessing: no encoding, just copy raw coefficients
|
||||
static size_t preprocess_coefficients_raw(int16_t *coeffs_y, int16_t *coeffs_co, int16_t *coeffs_cg, int16_t *coeffs_alpha,
|
||||
int coeff_count, int channel_layout, uint8_t *output_buffer) {
|
||||
const channel_layout_config_t *config = &channel_layouts[channel_layout];
|
||||
size_t offset = 0;
|
||||
|
||||
// Copy each active channel's coefficients directly to output buffer
|
||||
if (config->has_y && coeffs_y) {
|
||||
memcpy(output_buffer + offset, coeffs_y, coeff_count * sizeof(int16_t));
|
||||
offset += coeff_count * sizeof(int16_t);
|
||||
}
|
||||
if (config->has_co && coeffs_co) {
|
||||
memcpy(output_buffer + offset, coeffs_co, coeff_count * sizeof(int16_t));
|
||||
offset += coeff_count * sizeof(int16_t);
|
||||
}
|
||||
if (config->has_cg && coeffs_cg) {
|
||||
memcpy(output_buffer + offset, coeffs_cg, coeff_count * sizeof(int16_t));
|
||||
offset += coeff_count * sizeof(int16_t);
|
||||
}
|
||||
if (config->has_alpha && coeffs_alpha) {
|
||||
memcpy(output_buffer + offset, coeffs_alpha, coeff_count * sizeof(int16_t));
|
||||
offset += coeff_count * sizeof(int16_t);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// EZBC preprocessing: encode each channel with embedded zero block coding
|
||||
static size_t preprocess_coefficients_ezbc(int16_t *coeffs_y, int16_t *coeffs_co, int16_t *coeffs_cg, int16_t *coeffs_alpha,
|
||||
int coeff_count, int width, int height, int channel_layout,
|
||||
@@ -6555,28 +6635,73 @@ static size_t preprocess_coefficients_ezbc(int16_t *coeffs_y, int16_t *coeffs_co
|
||||
return total_size;
|
||||
}
|
||||
|
||||
// Wrapper: select between EZBC and twobit-map based on encoder settings
|
||||
static size_t preprocess_coefficients_variable_layout(int enable_ezbc, int width, int height,
|
||||
// Wrapper: select preprocessing mode based on encoder settings
|
||||
static size_t preprocess_coefficients_variable_layout(preprocess_mode_t preprocess_mode, int width, int height,
|
||||
int16_t *coeffs_y, int16_t *coeffs_co, int16_t *coeffs_cg, int16_t *coeffs_alpha,
|
||||
int coeff_count, int channel_layout, uint8_t *output_buffer) {
|
||||
if (enable_ezbc) {
|
||||
return preprocess_coefficients_ezbc(coeffs_y, coeffs_co, coeffs_cg, coeffs_alpha,
|
||||
coeff_count, width, height, channel_layout, output_buffer);
|
||||
} else {
|
||||
return preprocess_coefficients_twobitmap(coeffs_y, coeffs_co, coeffs_cg, coeffs_alpha,
|
||||
coeff_count, channel_layout, output_buffer);
|
||||
switch (preprocess_mode) {
|
||||
case PREPROCESS_EZBC:
|
||||
return preprocess_coefficients_ezbc(coeffs_y, coeffs_co, coeffs_cg, coeffs_alpha,
|
||||
coeff_count, width, height, channel_layout, output_buffer);
|
||||
case PREPROCESS_RAW:
|
||||
return preprocess_coefficients_raw(coeffs_y, coeffs_co, coeffs_cg, coeffs_alpha,
|
||||
coeff_count, channel_layout, output_buffer);
|
||||
case PREPROCESS_TWOBITMAP:
|
||||
default:
|
||||
return preprocess_coefficients_twobitmap(coeffs_y, coeffs_co, coeffs_cg, coeffs_alpha,
|
||||
coeff_count, channel_layout, output_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Unified GOP preprocessing: single significance map for all frames and channels
|
||||
// Layout (twobit-map): [All_Y_maps][All_Co_maps][All_Cg_maps][All_Y_values][All_Co_values][All_Cg_values]
|
||||
// Layout (EZBC): [frame0_size(4)][frame0_ezbc][frame1_size(4)][frame1_ezbc]...
|
||||
// Layout (raw): [All_Y_coeffs][All_Co_coeffs][All_Cg_coeffs]
|
||||
// This enables optimal cross-frame compression in the temporal dimension
|
||||
static size_t preprocess_gop_unified(int enable_ezbc, int16_t **quant_y, int16_t **quant_co, int16_t **quant_cg,
|
||||
static size_t preprocess_gop_unified(preprocess_mode_t preprocess_mode, int16_t **quant_y, int16_t **quant_co, int16_t **quant_cg,
|
||||
int num_frames, int num_pixels, int width, int height, int channel_layout,
|
||||
uint8_t *output_buffer) {
|
||||
const channel_layout_config_t *config = &channel_layouts[channel_layout];
|
||||
|
||||
// Raw mode: just concatenate all coefficients
|
||||
if (preprocess_mode == PREPROCESS_RAW) {
|
||||
size_t offset = 0;
|
||||
|
||||
// Copy all Y frames
|
||||
if (config->has_y && quant_y) {
|
||||
for (int frame = 0; frame < num_frames; frame++) {
|
||||
if (quant_y[frame]) {
|
||||
memcpy(output_buffer + offset, quant_y[frame], num_pixels * sizeof(int16_t));
|
||||
offset += num_pixels * sizeof(int16_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy all Co frames
|
||||
if (config->has_co && quant_co) {
|
||||
for (int frame = 0; frame < num_frames; frame++) {
|
||||
if (quant_co[frame]) {
|
||||
memcpy(output_buffer + offset, quant_co[frame], num_pixels * sizeof(int16_t));
|
||||
offset += num_pixels * sizeof(int16_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy all Cg frames
|
||||
if (config->has_cg && quant_cg) {
|
||||
for (int frame = 0; frame < num_frames; frame++) {
|
||||
if (quant_cg[frame]) {
|
||||
memcpy(output_buffer + offset, quant_cg[frame], num_pixels * sizeof(int16_t));
|
||||
offset += num_pixels * sizeof(int16_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// EZBC mode: encode each frame separately with EZBC
|
||||
if (enable_ezbc) {
|
||||
if (preprocess_mode == PREPROCESS_EZBC) {
|
||||
size_t total_size = 0;
|
||||
uint8_t *write_ptr = output_buffer;
|
||||
|
||||
@@ -6601,7 +6726,6 @@ static size_t preprocess_gop_unified(int enable_ezbc, int16_t **quant_y, int16_t
|
||||
}
|
||||
|
||||
// Twobit-map mode: original unified GOP preprocessing
|
||||
const channel_layout_config_t *config = &channel_layouts[channel_layout];
|
||||
const int map_bytes_per_frame = (num_pixels * 2 + 7) / 8; // 2 bits per coefficient
|
||||
const int total_coeffs = num_pixels * num_frames;
|
||||
|
||||
@@ -7205,7 +7329,7 @@ static size_t serialise_tile_data(tav_encoder_t *enc, int tile_x, int tile_y,
|
||||
|
||||
if (mode == TAV_MODE_INTRA) {
|
||||
// INTRA mode: quantise coefficients directly and store for future reference
|
||||
if (enc->enable_ezbc) {
|
||||
if (enc->preprocess_mode == PREPROCESS_EZBC) {
|
||||
// EZBC mode: Quantize with perceptual weighting but no normalization (division by quantizer)
|
||||
// fprintf(stderr, "[EZBC-QUANT-INTRA] Using perceptual quantization without normalization\n");
|
||||
quantise_dwt_coefficients_perceptual_per_coeff_no_normalisation(enc, (float*)tile_y_data, quantised_y, tile_size, this_frame_qY, enc->width, enc->height, enc->decomp_levels, 0, enc->frame_count);
|
||||
@@ -7327,7 +7451,7 @@ static size_t serialise_tile_data(tav_encoder_t *enc, int tile_x, int tile_y,
|
||||
}*/
|
||||
|
||||
// Preprocess and write quantised coefficients using variable channel layout concatenated significance maps
|
||||
size_t total_compressed_size = preprocess_coefficients_variable_layout(enc->enable_ezbc, enc->width, enc->height,
|
||||
size_t total_compressed_size = preprocess_coefficients_variable_layout(enc->preprocess_mode, enc->width, enc->height,
|
||||
quantised_y, quantised_co, quantised_cg, NULL,
|
||||
tile_size, enc->channel_layout, buffer + offset);
|
||||
offset += total_compressed_size;
|
||||
@@ -7953,8 +8077,8 @@ static int write_tav_header(tav_encoder_t *enc) {
|
||||
fputc(enc->quality_level+1, enc->output_fp);
|
||||
fputc(enc->channel_layout, enc->output_fp);
|
||||
|
||||
// Entropy Coder (0 = Twobit-map, 1 = EZBC)
|
||||
fputc(enc->enable_ezbc ? 1 : 0, enc->output_fp);
|
||||
// Entropy Coder (0 = Twobit-map, 1 = EZBC, 2 = Raw)
|
||||
fputc(enc->preprocess_mode, enc->output_fp);
|
||||
|
||||
// Reserved bytes (2 bytes)
|
||||
fputc(0, enc->output_fp);
|
||||
@@ -10367,6 +10491,7 @@ int main(int argc, char *argv[]) {
|
||||
{"native-audio", no_argument, 0, 1027},
|
||||
{"native-audio-format", no_argument, 0, 1027},
|
||||
{"tad-audio", no_argument, 0, 1028},
|
||||
{"raw-coeffs", no_argument, 0, 1029},
|
||||
{"single-pass", no_argument, 0, 1050}, // disable two-pass encoding with wavelet-based scene detection
|
||||
{"help", no_argument, 0, '?'},
|
||||
{0, 0, 0, 0}
|
||||
@@ -10538,7 +10663,7 @@ int main(int argc, char *argv[]) {
|
||||
break;
|
||||
case 1020: // --mc-ezbc
|
||||
enc->temporal_enable_mcezbc = 1;
|
||||
enc->enable_ezbc = 1;
|
||||
enc->preprocess_mode = PREPROCESS_EZBC;
|
||||
printf("MC-EZBC block-based motion compensation enabled (requires --temporal-dwt)\n");
|
||||
break;
|
||||
case 1021: // --residual-coding
|
||||
@@ -10577,7 +10702,7 @@ int main(int argc, char *argv[]) {
|
||||
printf("GOP size set to %d frames\n", enc->residual_coding_gop_size);
|
||||
break;
|
||||
case 1025: // --ezbc
|
||||
enc->enable_ezbc = 1;
|
||||
enc->preprocess_mode = PREPROCESS_EZBC;
|
||||
printf("EZBC (Embedded Zero Block Coding) enabled for significance maps\n");
|
||||
break;
|
||||
case 1026: // --separate-audio-track
|
||||
@@ -10594,6 +10719,10 @@ int main(int argc, char *argv[]) {
|
||||
enc->pcm8_audio = 0;
|
||||
printf("TAD audio mode enabled (packet 0x24, quality follows -q)\n");
|
||||
break;
|
||||
case 1029: // --raw-coeffs
|
||||
enc->preprocess_mode = PREPROCESS_RAW;
|
||||
printf("Raw coefficient mode enabled (no significance map preprocessing)\n");
|
||||
break;
|
||||
case 1050: // --single-pass
|
||||
enc->two_pass_mode = 0;
|
||||
printf("Two-pass wavelet-based scene change detection disabled\n");
|
||||
|
||||
Reference in New Issue
Block a user