优化打印算法
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user