优化打印算法

This commit is contained in:
admin
2026-02-24 23:34:10 +08:00
parent 92ac92f514
commit e9ac05592c
2 changed files with 142 additions and 16 deletions

View File

@@ -40,6 +40,75 @@ static void *calloc_prefer_psram(size_t n, size_t size) {
return ptr;
}
static uint8_t sample_gray8_scaled(const uint8_t *gray,
uint16_t src_width,
uint16_t src_height,
bool scale_to_width,
uint16_t dst_height,
uint16_t x,
uint16_t y) {
uint16_t src_y = scale_to_width
? (uint16_t)(((uint32_t)y * src_height) / dst_height)
: y;
if (src_y >= src_height) {
src_y = (uint16_t)(src_height - 1);
}
uint16_t src_x = scale_to_width
? (uint16_t)(((uint32_t)x * src_width) / RASTER_WIDTH)
: x;
if (src_x >= src_width) {
src_x = (uint16_t)(src_width - 1);
}
return gray[(size_t)src_y * src_width + src_x];
}
static uint8_t gray8_to_work_luma(uint8_t gray, bool invert) {
return invert ? (uint8_t)(255u - gray) : gray;
}
static uint8_t compute_otsu_threshold(const uint32_t hist[256], uint32_t total_count) {
if (hist == NULL || total_count == 0) {
return 160;
}
uint64_t all_sum = 0;
for (uint32_t i = 0; i < 256; ++i) {
all_sum += (uint64_t)i * hist[i];
}
uint64_t bg_sum = 0;
uint32_t bg_count = 0;
double max_between_var = -1.0;
uint8_t best_thr = 160;
for (uint32_t t = 0; t < 256; ++t) {
bg_count += hist[t];
if (bg_count == 0) {
continue;
}
uint32_t fg_count = total_count - bg_count;
if (fg_count == 0) {
break;
}
bg_sum += (uint64_t)t * hist[t];
double bg_mean = (double)bg_sum / (double)bg_count;
double fg_mean = (double)(all_sum - bg_sum) / (double)fg_count;
double mean_diff = bg_mean - fg_mean;
double between_var = (double)bg_count * (double)fg_count * mean_diff * mean_diff;
if (between_var > max_between_var) {
max_between_var = between_var;
best_thr = (uint8_t)t;
}
}
return best_thr;
}
esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
uint16_t src_width,
uint16_t src_height,
@@ -105,30 +174,81 @@ esp_err_t raster_tools_convert_gray8_to_raster_384(const uint8_t *gray,
return ESP_ERR_NO_MEM;
}
uint32_t hist[256] = {0};
uint64_t sum_luma = 0;
uint32_t total_px = (uint32_t)RASTER_WIDTH * (uint32_t)dst_height;
for (uint16_t y = 0; y < dst_height; ++y) {
uint16_t src_y = scale_to_width
? (uint16_t)(((uint32_t)y * src_height) / dst_height)
: y;
if (src_y >= src_height) {
src_y = (uint16_t)(src_height - 1);
}
for (uint16_t x = 0; x < RASTER_WIDTH; ++x) {
uint16_t src_x = scale_to_width
? (uint16_t)(((uint32_t)x * src_width) / RASTER_WIDTH)
: x;
if (src_x >= src_width) {
src_x = (uint16_t)(src_width - 1);
uint8_t src_v = sample_gray8_scaled(gray, src_width, src_height, scale_to_width, dst_height, x, y);
uint8_t work_v = gray8_to_work_luma(src_v, invert);
hist[work_v]++;
sum_luma += work_v;
}
}
uint8_t otsu_thr = compute_otsu_threshold(hist, total_px);
uint8_t mean_luma = total_px > 0 ? (uint8_t)(sum_luma / total_px) : (uint8_t)160;
/* Keep backward compatibility: threshold still works as bias around 160. */
int final_thr = (int)otsu_thr + (((int)threshold - 160) / 2);
/* White-bias to reduce large black blocks on thermal paper. */
if (mean_luma < 112) {
final_thr -= 10;
} else if (mean_luma > 182) {
final_thr += 6;
}
if (final_thr < 40) {
final_thr = 40;
} else if (final_thr > 220) {
final_thr = 220;
}
int16_t *err_curr = (int16_t *)calloc_prefer_psram(RASTER_WIDTH + 2u, sizeof(int16_t));
int16_t *err_next = (int16_t *)calloc_prefer_psram(RASTER_WIDTH + 2u, sizeof(int16_t));
bool use_dither = (err_curr != NULL && err_next != NULL);
for (uint16_t y = 0; y < dst_height; ++y) {
for (uint16_t x = 0; x < RASTER_WIDTH; ++x) {
uint8_t src_v = sample_gray8_scaled(gray, src_width, src_height, scale_to_width, dst_height, x, y);
int luma = (int)gray8_to_work_luma(src_v, invert);
if (use_dither) {
luma += err_curr[x + 1u];
if (luma < 0) {
luma = 0;
} else if (luma > 255) {
luma = 255;
}
}
uint8_t v = gray[(size_t)src_y * src_width + src_x];
bool black = invert ? (v > threshold) : (v < threshold);
bool black = luma < final_thr;
if (black) {
set_black(raster, RASTER_WIDTH, x, y);
}
if (use_dither) {
int quantized = black ? 0 : 255;
int e = luma - quantized;
if ((x + 1u) < RASTER_WIDTH) {
err_curr[x + 2u] += (int16_t)((e * 7) / 16);
}
err_next[x] += (int16_t)((e * 3) / 16);
err_next[x + 1u] += (int16_t)((e * 5) / 16);
err_next[x + 2u] += (int16_t)(e / 16);
}
}
if (use_dither) {
int16_t *tmp = err_curr;
err_curr = err_next;
err_next = tmp;
memset(err_next, 0, (RASTER_WIDTH + 2u) * sizeof(int16_t));
}
}
free(err_curr);
free(err_next);
*out_width = dst_width;
*out_height = dst_height;
*out_raster = raster;