文章目录
头文件
c
// ==============================================
// File: JBIG2Encoder.h
// ==============================================
#ifndef JBIG2ENCODER_H
#define JBIG2ENCODER_H
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
class JBIG2Encoder {
public:
enum class Mode {
GENERIC, // 通用区域编码
SYMBOL // 符号模式(文本区域编码)
};
struct PageData {
std::vector<uint8_t> image_data;
int width;
int height;
int dpi;
int image_depth; // 1, 8, 24 等
// 添加构造函数
PageData() : width(0), height(0), dpi(0), image_depth(1) {}
PageData(const std::vector<uint8_t>& data, int w, int h, int d = 0, int depth = 1)
: image_data(data), width(w), height(h), dpi(d), image_depth(depth) {
}
PageData(std::vector<uint8_t>&& data, int w, int h, int d = 0, int depth = 1)
: image_data(std::move(data)), width(w), height(h), dpi(d), image_depth(depth) {
}
};
struct Options {
Mode mode = Mode::GENERIC;
bool pdf_mode = false;
bool duplicate_line_removal = false;
bool auto_threshold = false;
bool use_hash = true;
bool global_thresholding = false;
bool verbose = false;
// 阈值参数
float threshold = 0.92f;
float weight = 0.5f;
int bw_threshold = 200;
// 图像处理
int upsample_factor = 1; // 1, 2, 4
int image_depth = 1; // 输入图像深度
};
JBIG2Encoder();
~JBIG2Encoder();
// 编码单页
std::vector<uint8_t> EncodePage(const std::vector<uint8_t>& image_data,
int width, int height,
int dpi = 0,
const Options& options = Options());
// 编码多页文档
std::vector<uint8_t> EncodeDocument(const std::vector<PageData>& pages,
const Options& options = Options());
// 编码并保存到文件
bool EncodeToFile(const std::vector<uint8_t>& image_data,
int width, int height,
const std::string& filename,
int dpi = 0,
const Options& options = Options());
// 获取错误信息
std::string GetLastError() const;
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
#endif // JBIG2ENCODER_H
实现文件
cpp
// ==============================================
// File: JBIG2Encoder.cpp (修正版)
// ==============================================
#include "JBIG2Encoder.h"
#include <cstring>
#include <fstream>
#include <memory>
#include <vector>
#include <iostream>
#include <cstdlib>
#include <sstream>
#include <algorithm>
// libjbig2enc 头文件
#include "jbig2enc.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <arpa/inet.h>
#endif
// Leptonica 头文件
extern "C" {
#include "leptonica/allheaders.h"
#if (LIBLEPT_MAJOR_VERSION == 1 && LIBLEPT_MINOR_VERSION >= 83) || LIBLEPT_MAJOR_VERSION > 1
#include "leptonica/pix_internal.h"
#endif
}
class JBIG2Encoder::Impl {
public:
Impl() : last_error_(""), ctx_(nullptr) {}
~Impl() {
if (ctx_) {
jbig2_destroy(ctx_);
ctx_ = nullptr;
}
}
std::vector<uint8_t> EncodePage(const std::vector<uint8_t>& image_data,
int width, int height,
int dpi,
const Options& options) {
last_error_.clear();
if (!ValidateInput(image_data, width, height, options)) {
return {};
}
// 创建 PIX
PIX* pix = CreatePixFromMemory(image_data, width, height, options);
if (!pix) {
SetError("Failed to create PIX from memory data", options.verbose);
return {};
}
if (dpi > 0) {
pixSetResolution(pix, dpi, dpi);
}
std::vector<uint8_t> result;
if (options.mode == Mode::GENERIC) {
result = EncodeGeneric(pix, options);
}
else {
result = EncodeSymbolSinglePage(pix, options);
}
pixDestroy(&pix);
return result;
}
std::vector<uint8_t> EncodeDocument(const std::vector<PageData>& pages,
const Options& options) {
last_error_.clear();
if (pages.empty()) {
SetError("No pages to encode", options.verbose);
return {};
}
// 清理之前的上下文
if (ctx_) {
jbig2_destroy(ctx_);
ctx_ = nullptr;
}
// 初始化上下文
ctx_ = jbig2_init(options.threshold, options.weight, 0, 0, !options.pdf_mode, -1);
if (!ctx_) {
SetError("Failed to initialize JBIG2 encoder context", options.verbose);
return {};
}
// 添加所有页面
for (size_t i = 0; i < pages.size(); ++i) {
const PageData& page = pages[i];
if (!ValidateInput(page.image_data, page.width, page.height, options)) {
jbig2_destroy(ctx_);
ctx_ = nullptr;
return {};
}
PIX* pix = CreatePixFromMemory(page.image_data, page.width, page.height, options);
if (!pix) {
SetError("Failed to create PIX for page " + std::to_string(i), options.verbose);
jbig2_destroy(ctx_);
ctx_ = nullptr;
return {};
}
if (page.dpi > 0) {
pixSetResolution(pix, page.dpi, page.dpi);
}
jbig2_add_page(ctx_, pix);
pixDestroy(&pix);
}
if (options.auto_threshold) {
if (options.use_hash) {
jbig2enc_auto_threshold_using_hash(ctx_);
}
else {
jbig2enc_auto_threshold(ctx_);
}
}
// 获取完整文档编码
std::vector<uint8_t> result;
uint8_t* encoded_data = nullptr;
int encoded_length = 0;
encoded_data = jbig2_pages_complete(ctx_, &encoded_length);
if (encoded_data && encoded_length > 0) {
result.assign(encoded_data, encoded_data + encoded_length);
free(encoded_data);
}
else {
SetError("Encoding produced no data", options.verbose);
}
jbig2_destroy(ctx_);
ctx_ = nullptr;
return result;
}
bool EncodeToFile(const std::vector<uint8_t>& image_data,
int width, int height,
const std::string& filename,
int dpi,
const Options& options) {
auto encoded = EncodePage(image_data, width, height, dpi, options);
if (encoded.empty()) {
return false;
}
std::ofstream file(filename, std::ios::binary);
if (!file) {
SetError("Cannot open file for writing: " + filename, options.verbose);
return false;
}
file.write(reinterpret_cast<const char*>(encoded.data()), encoded.size());
if (!file.good()) {
SetError("Failed to write file: " + filename, options.verbose);
return false;
}
return true;
}
std::string GetLastError() const {
return last_error_;
}
private:
mutable std::string last_error_;
jbig2ctx* ctx_;
void SetError(const std::string& msg, bool verbose = false) const {
last_error_ = msg;
if (verbose) {
std::cerr << "JBIG2Encoder Error: " << msg << std::endl;
}
}
bool ValidateInput(const std::vector<uint8_t>& data,
int width, int height,
const Options& options) const {
if (data.empty()) {
SetError("Input data is empty", options.verbose);
return false;
}
if (width <= 0 || height <= 0) {
SetError("Invalid dimensions: " + std::to_string(width) + "x" + std::to_string(height), options.verbose);
return false;
}
// 对于二值图像,计算最小期望大小
int stride = (width + 7) / 8;
size_t min_expected = static_cast<size_t>(stride) * static_cast<size_t>(height);
if (data.size() < min_expected) {
SetError("Input data too small. Expected at least " +
std::to_string(min_expected) + " bytes, got " +
std::to_string(data.size()), options.verbose);
return false;
}
return true;
}
PIX* CreatePixFromMemory(const std::vector<uint8_t>& data,
int width, int height,
const Options& options) {
// 直接创建二值图像(假设输入已经是1bpp二值数据)
// 这是最简单且最可靠的方法
int stride = (width + 7) / 8;
PIX* pix = pixCreate(width, height, 1);
if (!pix) {
return nullptr;
}
l_uint32* pix_data = pixGetData(pix);
int pix_wpl = pixGetWpl(pix);
if (!pix_data) {
pixDestroy(&pix);
return nullptr;
}
// 复制数据
for (int y = 0; y < height; ++y) {
l_uint32* dst_line = pix_data + y * pix_wpl;
const uint8_t* src_line = data.data() + static_cast<size_t>(y) * stride;
// 清零当前行
for (int w = 0; w < pix_wpl; ++w)
dst_line[w] = 0;
// 设置位
for (int x = 0; x < width; ++x) {
int byte_idx = x / 8;
int bit_idx = 7 - (x % 8); // high-bit first
if (src_line[byte_idx] & (1u << bit_idx)) {
SET_DATA_BIT(dst_line, x);
}
}
}
return pix;
}
std::vector<uint8_t> EncodeGeneric(PIX* pix, const Options& options) {
std::vector<uint8_t> result;
int length = 0;
uint8_t* encoded = jbig2_encode_generic(
pix,
!options.pdf_mode, // full headers
0, 0, // x, y offset
options.duplicate_line_removal,
&length);
if (encoded && length > 0) {
result.assign(encoded, encoded + length);
free(encoded);
}
else {
SetError("Generic encoding failed", options.verbose);
}
return result;
}
std::vector<uint8_t> EncodeSymbolSinglePage(PIX* pix, const Options& options) {
std::vector<uint8_t> result;
// 为单页创建临时上下文
jbig2ctx* local_ctx = jbig2_init(options.threshold, options.weight, 0, 0,
!options.pdf_mode, -1);
if (!local_ctx) {
SetError("Failed to create symbol encoder context", options.verbose);
return result;
}
jbig2_add_page(local_ctx, pix);
if (options.auto_threshold) {
if (options.use_hash) {
jbig2enc_auto_threshold_using_hash(local_ctx);
}
else {
jbig2enc_auto_threshold(local_ctx);
}
}
int length = 0;
uint8_t* encoded = nullptr;
if (options.pdf_mode) {
encoded = jbig2_pages_complete(local_ctx, &length);
}
else {
encoded = jbig2_produce_page(local_ctx, 0, -1, -1, &length);
}
if (encoded && length > 0) {
result.assign(encoded, encoded + length);
free(encoded);
}
else {
SetError("Symbol encoding failed", options.verbose);
}
jbig2_destroy(local_ctx);
return result;
}
};
// ==============================================
// JBIG2Encoder public wrapper
// ==============================================
JBIG2Encoder::JBIG2Encoder() : impl_(std::make_unique<Impl>()) {}
JBIG2Encoder::~JBIG2Encoder() = default;
std::vector<uint8_t> JBIG2Encoder::EncodePage(const std::vector<uint8_t>& image_data,
int width, int height,
int dpi,
const Options& options) {
return impl_->EncodePage(image_data, width, height, dpi, options);
}
std::vector<uint8_t> JBIG2Encoder::EncodeDocument(const std::vector<PageData>& pages,
const Options& options) {
return impl_->EncodeDocument(pages, options);
}
bool JBIG2Encoder::EncodeToFile(const std::vector<uint8_t>& image_data,
int width, int height,
const std::string& filename,
int dpi,
const Options& options) {
return impl_->EncodeToFile(image_data, width, height, filename, dpi, options);
}
std::string JBIG2Encoder::GetLastError() const {
return impl_->GetLastError();
}
测试文件
cpp
#include "JBIG2Encoder.h"
#include <iostream>
#include <vector>
#include <fstream>
#include <random>
#include <string>
#include <iomanip>
#include <filesystem>
#include <cmath>
namespace fs = std::filesystem;
// 生成随机种子
std::random_device rd;
std::mt19937 gen(rd());
// 基本形状函数
void DrawLine(std::vector<uint8_t>& buf, int w, int h, int x1, int y1, int x2, int y2) {
int stride = (w + 7) / 8;
int dx = std::abs(x2 - x1);
int dy = std::abs(y2 - y1);
int sx = (x1 < x2) ? 1 : -1;
int sy = (y1 < y2) ? 1 : -1;
int err = dx - dy;
while (true) {
if (x1 >= 0 && x1 < w && y1 >= 0 && y1 < h) {
int byte_idx = y1 * stride + (x1 / 8);
int bit_pos = 7 - (x1 % 8);
buf[byte_idx] |= (1 << bit_pos);
}
if (x1 == x2 && y1 == y2) break;
int e2 = err * 2;
if (e2 > -dy) {
err -= dy;
x1 += sx;
}
if (e2 < dx) {
err += dx;
y1 += sy;
}
}
}
void DrawCircle(std::vector<uint8_t>& buf, int w, int h, int cx, int cy, int r) {
int stride = (w + 7) / 8;
int x = 0;
int y = r;
int d = 3 - 2 * r;
auto drawPoints = [&](int x, int y) {
std::vector<std::pair<int, int>> points = {
{cx + x, cy + y}, {cx - x, cy + y},
{cx + x, cy - y}, {cx - x, cy - y},
{cx + y, cy + x}, {cx - y, cy + x},
{cx + y, cy - x}, {cx - y, cy - x}
};
for (auto& p : points) {
int px = p.first;
int py = p.second;
if (px >= 0 && px < w && py >= 0 && py < h) {
int byte_idx = py * stride + (px / 8);
int bit_pos = 7 - (px % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
};
drawPoints(x, y);
while (y >= x) {
x++;
if (d > 0) {
y--;
d = d + 4 * (x - y) + 10;
}
else {
d = d + 4 * x + 6;
}
drawPoints(x, y);
}
}
void DrawRect(std::vector<uint8_t>& buf, int w, int h, int x1, int y1, int x2, int y2, bool filled = false) {
int stride = (w + 7) / 8;
if (filled) {
for (int y = std::max(0, y1); y <= std::min(h - 1, y2); ++y) {
for (int x = std::max(0, x1); x <= std::min(w - 1, x2); ++x) {
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
}
else {
// 只画边框
for (int x = x1; x <= x2; ++x) {
if (y1 >= 0 && y1 < h && x >= 0 && x < w) {
int byte_idx = y1 * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
if (y2 >= 0 && y2 < h && x >= 0 && x < w) {
int byte_idx = y2 * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
for (int y = y1; y <= y2; ++y) {
if (x1 >= 0 && x1 < w && y >= 0 && y < h) {
int byte_idx = y * stride + (x1 / 8);
int bit_pos = 7 - (x1 % 8);
buf[byte_idx] |= (1 << bit_pos);
}
if (x2 >= 0 && x2 < w && y >= 0 && y < h) {
int byte_idx = y * stride + (x2 / 8);
int bit_pos = 7 - (x2 % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
}
}
void DrawTriangle(std::vector<uint8_t>& buf, int w, int h, int x1, int y1, int x2, int y2, int x3, int y3, bool filled = false) {
if (filled) {
// 填充三角形(简化版)
auto edgeFunc = [](int ax, int ay, int bx, int by, int cx, int cy) {
return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
};
int minX = std::min({ x1, x2, x3 });
int maxX = std::max({ x1, x2, x3 });
int minY = std::min({ y1, y2, y3 });
int maxY = std::max({ y1, y2, y3 });
for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) {
float w0 = edgeFunc(x2, y2, x3, y3, x, y);
float w1 = edgeFunc(x3, y3, x1, y1, x, y);
float w2 = edgeFunc(x1, y1, x2, y2, x, y);
if (w0 >= 0 && w1 >= 0 && w2 >= 0) {
if (x >= 0 && x < w && y >= 0 && y < h) {
int stride = (w + 7) / 8;
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
}
}
}
else {
// 只画边框
DrawLine(buf, w, h, x1, y1, x2, y2);
DrawLine(buf, w, h, x2, y2, x3, y3);
DrawLine(buf, w, h, x3, y3, x1, y1);
}
}
// 生成几何形状测试图像
std::vector<uint8_t> CreateGeometricShapes(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0x00);
std::uniform_int_distribution<> coord_dis(0, std::max(w, h));
std::uniform_int_distribution<> size_dis(20, 200);
std::uniform_int_distribution<> bool_dis(0, 1);
// 画随机线条
for (int i = 0; i < 50; ++i) {
int x1 = coord_dis(gen) % w;
int y1 = coord_dis(gen) % h;
int x2 = coord_dis(gen) % w;
int y2 = coord_dis(gen) % h;
DrawLine(buf, w, h, x1, y1, x2, y2);
}
// 画随机圆形
for (int i = 0; i < 20; ++i) {
int cx = coord_dis(gen) % w;
int cy = coord_dis(gen) % h;
int r = size_dis(gen) / 2;
DrawCircle(buf, w, h, cx, cy, r);
}
// 画随机矩形
for (int i = 0; i < 15; ++i) {
int x1 = coord_dis(gen) % (w - 50);
int y1 = coord_dis(gen) % (h - 50);
int width = size_dis(gen);
int height = size_dis(gen);
bool filled = bool_dis(gen);
DrawRect(buf, w, h, x1, y1, x1 + width, y1 + height, filled);
}
// 画随机三角形
for (int i = 0; i < 10; ++i) {
int x1 = coord_dis(gen) % w;
int y1 = coord_dis(gen) % h;
int x2 = coord_dis(gen) % w;
int y2 = coord_dis(gen) % h;
int x3 = coord_dis(gen) % w;
int y3 = coord_dis(gen) % h;
bool filled = bool_dis(gen);
DrawTriangle(buf, w, h, x1, y1, x2, y2, x3, y3, filled);
}
return buf;
}
// 生成电路板图案
std::vector<uint8_t> CreateCircuitBoardPattern(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0x00);
// 画网格
for (int y = 0; y < h; y += 40) {
for (int x = 0; x < w; ++x) {
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
for (int x = 0; x < w; x += 40) {
for (int y = 0; y < h; ++y) {
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
// 画焊盘
for (int y = 20; y < h; y += 40) {
for (int x = 20; x < w; x += 40) {
DrawCircle(buf, w, h, x, y, 5);
}
}
// 画集成电路
for (int y = 100; y < h - 100; y += 150) {
for (int x = 100; x < w - 100; x += 150) {
DrawRect(buf, w, h, x, y, x + 80, y + 40, false);
// 引脚
for (int i = 0; i < 8; ++i) {
int px = x + 5;
int py = y + 5 + i * 4;
DrawLine(buf, w, h, px, py, px + 10, py);
}
}
}
return buf;
}
// 生成机械图纸图案
std::vector<uint8_t> CreateMechanicalDrawing(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0x00);
// 中心线
DrawLine(buf, w, h, w / 2, 0, w / 2, h - 1);
DrawLine(buf, w, h, 0, h / 2, w - 1, h / 2);
// 坐标系刻度
for (int x = 0; x < w; x += 50) {
for (int y = h / 2 - 5; y <= h / 2 + 5; ++y) {
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
for (int y = 0; y < h; y += 50) {
for (int x = w / 2 - 5; x <= w / 2 + 5; ++x) {
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
// 机械零件
int centerX = w / 2;
int centerY = h / 2;
// 圆形零件
DrawCircle(buf, w, h, centerX - 150, centerY, 80);
DrawCircle(buf, w, h, centerX - 150, centerY, 30);
// 方形零件
DrawRect(buf, w, h, centerX + 50, centerY - 100, centerX + 250, centerY + 100, false);
DrawRect(buf, w, h, centerX + 100, centerY - 50, centerX + 200, centerY + 50, false);
// 三角形零件
DrawTriangle(buf, w, h, centerX - 50, centerY - 100, centerX + 50, centerY, centerX - 50, centerY + 100, false);
// 尺寸标注
DrawLine(buf, w, h, centerX - 230, centerY - 100, centerX - 230, centerY + 100);
DrawLine(buf, w, h, centerX - 235, centerY - 100, centerX - 225, centerY - 100);
DrawLine(buf, w, h, centerX - 235, centerY + 100, centerX - 225, centerY + 100);
return buf;
}
// 生成建筑平面图
std::vector<uint8_t> CreateArchitecturePlan(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0x00);
// 外框
DrawRect(buf, w, h, 50, 50, w - 50, h - 50, false);
// 房间1 - 客厅
DrawRect(buf, w, h, 100, 100, 500, 300, false);
// 房间2 - 卧室
DrawRect(buf, w, h, 600, 100, 900, 300, false);
// 房间3 - 厨房
DrawRect(buf, w, h, 100, 400, 500, 600, false);
// 房间4 - 卫生间
DrawRect(buf, w, h, 600, 400, 900, 600, false);
// 门
// 客厅门
DrawRect(buf, w, h, 450, 300, 500, 320, false);
// 卧室门
DrawRect(buf, w, h, 600, 200, 620, 300, false);
// 窗户
for (int i = 0; i < 3; ++i) {
int x = 150 + i * 100;
DrawRect(buf, w, h, x, 50, x + 50, 100, false);
}
// 家具
// 沙发
DrawRect(buf, w, h, 120, 120, 300, 180, true);
// 床
DrawRect(buf, w, h, 620, 120, 850, 220, true);
// 桌子
DrawRect(buf, w, h, 120, 450, 300, 550, false);
DrawRect(buf, w, h, 200, 450, 220, 550, true);
DrawRect(buf, w, h, 120, 520, 300, 540, true);
return buf;
}
// 生成地图图案
std::vector<uint8_t> CreateMapPattern(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0x00);
// 河流
std::vector<std::pair<int, int>> river_points = {
{100, 100}, {200, 80}, {300, 150}, {400, 100},
{500, 200}, {600, 180}, {700, 250}, {800, 200}
};
for (size_t i = 0; i < river_points.size() - 1; ++i) {
int x1 = river_points[i].first;
int y1 = river_points[i].second;
int x2 = river_points[i + 1].first;
int y2 = river_points[i + 1].second;
// 画河流(双线)
DrawLine(buf, w, h, x1, y1, x2, y2);
DrawLine(buf, w, h, x1 + 5, y1 + 5, x2 + 5, y2 + 5);
}
// 道路
DrawLine(buf, w, h, 50, 300, 200, 300);
DrawLine(buf, w, h, 200, 300, 300, 400);
DrawLine(buf, w, h, 300, 400, 500, 400);
DrawLine(buf, w, h, 500, 400, 600, 300);
// 建筑
std::vector<std::pair<std::pair<int, int>, std::string>> buildings = {
{{100, 200}, "R"}, // 住宅
{{300, 300}, "C"}, // 商业
{{500, 250}, "I"}, // 工业
{{700, 350}, "P"}, // 公园
};
for (const auto& building : buildings) {
int x = building.first.first;
int y = building.first.second;
DrawRect(buf, w, h, x, y, x + 30, y + 20, true);
}
// 森林区域
for (int i = 0; i < 20; ++i) {
int x = 600 + (i % 5) * 40;
int y = 100 + (i / 5) * 40;
for (int j = 0; j < 3; ++j) {
DrawCircle(buf, w, h, x + j * 5, y, 2);
}
}
return buf;
}
// 生成科技图表
std::vector<uint8_t> CreateTechnologyChart(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0x00);
// 坐标轴
DrawLine(buf, w, h, 100, 100, 100, 500);
DrawLine(buf, w, h, 100, 500, 700, 500);
// 坐标刻度
for (int y = 100; y <= 500; y += 50) {
for (int x = 95; x <= 105; ++x) {
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
for (int x = 100; x <= 700; x += 50) {
for (int y = 495; y <= 505; ++y) {
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] |= (1 << bit_pos);
}
}
// 数据点
std::vector<std::pair<int, int>> data_points = {
{100, 400}, {200, 350}, {300, 300}, {400, 250},
{500, 200}, {600, 150}, {700, 100}
};
for (const auto& point : data_points) {
int x = point.first;
int y = point.second;
DrawCircle(buf, w, h, x, y, 5);
}
// 连接线
for (size_t i = 0; i < data_points.size() - 1; ++i) {
int x1 = data_points[i].first;
int y1 = data_points[i].second;
int x2 = data_points[i + 1].first;
int y2 = data_points[i + 1].second;
DrawLine(buf, w, h, x1, y1, x2, y2);
}
// 饼图
int centerX = 500;
int centerY = 200;
int radius = 100;
DrawCircle(buf, w, h, centerX, centerY, radius);
// 饼图扇区
std::vector<std::pair<float, float>> angles = {
{0.0f, 1.047f}, // 60度
{1.047f, 2.094f}, // 120度
{2.094f, 4.188f}, // 240度
};
for (const auto& angle : angles) {
int x1 = centerX + radius * cos(angle.first);
int y1 = centerY + radius * sin(angle.first);
int x2 = centerX + radius * cos(angle.second);
int y2 = centerY + radius * sin(angle.second);
DrawLine(buf, w, h, centerX, centerY, x1, y1);
DrawLine(buf, w, h, centerX, centerY, x2, y2);
}
// 柱状图
std::vector<int> bars = { 80, 120, 160, 200, 240 };
for (size_t i = 0; i < bars.size(); ++i) {
int x = 300 + i * 50;
int height = bars[i];
DrawRect(buf, w, h, x, 500 - height, x + 30, 500, true);
}
return buf;
}
// 生成重复图案(适合符号模式)
std::vector<uint8_t> CreateRepeatedPattern(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0xFF); // 白色背景
// 基本符号:小方块、圆圈、十字等
int symbol_size = 16;
for (int y = 0; y < h - symbol_size; y += symbol_size * 2) {
for (int x = 0; x < w - symbol_size; x += symbol_size * 2) {
// 绘制小方块
DrawRect(buf, w, h, x, y, x + symbol_size, y + symbol_size, true);
// 绘制小圆圈
DrawCircle(buf, w, h, x + symbol_size * 3 / 2, y + symbol_size / 2, symbol_size / 4);
// 绘制十字
int cx = x + symbol_size * 3 / 2;
int cy = y + symbol_size * 3 / 2;
DrawLine(buf, w, h, cx - symbol_size / 4, cy, cx + symbol_size / 4, cy);
DrawLine(buf, w, h, cx, cy - symbol_size / 4, cx, cy + symbol_size / 4);
}
}
return buf;
}
// 生成文档图像(包含标题、段落)- 增强版
std::vector<uint8_t> CreateDocumentImage(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0xFF); // 白底
// 标题
for (int y = 50; y < 100; ++y) {
int row_start = y * stride;
for (int x = 100; x < w - 100; ++x) {
int byte_idx = row_start + (x / 8);
int bit_pos = 7 - (x % 8);
// 加粗标题
if (y < 60 || y > 90 || (x >= 100 && x < 150) || (x >= w - 150 && x < w - 100)) {
buf[byte_idx] &= ~(1 << bit_pos);
}
}
}
// 段落
for (int para = 0; para < 5; ++para) {
int start_y = 150 + para * 100;
for (int y = start_y; y < start_y + 80; ++y) {
int row_start = y * stride;
for (int x = 100; x < w - 100; ++x) {
int byte_idx = row_start + (x / 8);
int bit_pos = 7 - (x % 8);
// 模拟文字行
if ((y - start_y) % 20 < 12) {
int char_width = 8;
int char_pos = (x - 100) / char_width;
if ((char_pos % 12) < 10) { // 每行10个字符
if ((x - 100) % char_width < 6) {
buf[byte_idx] &= ~(1 << bit_pos);
}
}
}
}
}
}
// 页脚
for (int y = h - 50; y < h - 20; ++y) {
int row_start = y * stride;
for (int x = 100; x < w - 100; x += 2) {
int byte_idx = row_start + (x / 8);
int bit_pos = 7 - (x % 8);
buf[byte_idx] &= ~(1 << bit_pos);
}
}
return buf;
}
// 生成随机二值图像(包含复杂图案)- 增强版
std::vector<uint8_t> CreateRandomBinaryImage(int w, int h, int pattern = 0) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0x00);
std::uniform_int_distribution<> dis(0, 255);
std::uniform_real_distribution<> prob(0.0, 1.0);
switch (pattern) {
case 0: // 随机噪声
for (size_t i = 0; i < buf.size(); ++i) {
buf[i] = static_cast<uint8_t>(dis(gen));
}
break;
case 1: // 网格图案
for (int y = 0; y < h; ++y) {
int row_start = y * stride;
for (int x = 0; x < w; ++x) {
int byte_idx = row_start + (x / 8);
int bit_pos = 7 - (x % 8);
if ((x % 20 < 2) || (y % 20 < 2)) {
buf[byte_idx] |= (1 << bit_pos);
}
else {
buf[byte_idx] &= ~(1 << bit_pos);
}
}
}
break;
case 2: // 几何形状组合
buf = CreateGeometricShapes(w, h);
break;
case 3: // 电路板图案
buf = CreateCircuitBoardPattern(w, h);
break;
case 4: // 机械图纸
buf = CreateMechanicalDrawing(w, h);
break;
case 5: // 建筑平面图
buf = CreateArchitecturePlan(w, h);
break;
case 6: // 地图
buf = CreateMapPattern(w, h);
break;
case 7: // 科技图表
buf = CreateTechnologyChart(w, h);
break;
}
return buf;
}
// 生成渐变图像
std::vector<uint8_t> CreateGradientImage(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0xFF);
for (int y = 0; y < h; ++y) {
int row_start = y * stride;
for (int x = 0; x < w; ++x) {
int byte_idx = row_start + (x / 8);
int bit_pos = 7 - (x % 8);
// 创建渐变效果
double x_norm = static_cast<double>(x) / w;
double y_norm = static_cast<double>(y) / h;
double gradient = 0.5 + 0.4 * sin(x_norm * 6.28) * sin(y_norm * 6.28);
if (gradient > 0.7) {
buf[byte_idx] &= ~(1 << bit_pos);
}
else {
buf[byte_idx] |= (1 << bit_pos);
}
}
}
return buf;
}
// 生成符号模式测试图像(小图,适合符号编码)
std::vector<uint8_t> CreateSymbolTestImage(int w, int h) {
int stride = (w + 7) / 8;
std::vector<uint8_t> buf(stride * h, 0xFF); // 白底黑字模式
// 创建重复的小图案
int pattern_width = 32;
int pattern_height = 32;
for (int y = 0; y < h; y += pattern_height) {
for (int x = 0; x < w; x += pattern_width) {
// 绘制一些简单形状作为符号
if ((x / pattern_width + y / pattern_height) % 2 == 0) {
// 方块
DrawRect(buf, w, h, x + 2, y + 2, x + pattern_width - 2, y + pattern_height - 2, false);
DrawCircle(buf, w, h, x + pattern_width / 2, y + pattern_height / 2, pattern_height / 4);
}
else {
// 十字
int cx = x + pattern_width / 2;
int cy = y + pattern_height / 2;
DrawLine(buf, w, h, cx - pattern_width / 4, cy, cx + pattern_width / 4, cy);
DrawLine(buf, w, h, cx, cy - pattern_height / 4, cx, cy + pattern_height / 4);
}
}
}
return buf;
}
// 测试函数 - 添加异常处理
bool TestSinglePage(JBIG2Encoder& encoder, const std::vector<uint8_t>& img,
int w, int h, const std::string& test_name,
const JBIG2Encoder::Options& opt, const fs::path& output_dir) {
std::cout << "\n=== 测试: " << test_name << " ===" << std::endl;
std::cout << "尺寸: " << w << "x" << h
<< " (" << (img.size() / 1024) << " KB)" << std::endl;
try {
auto enc = encoder.EncodePage(img, w, h, 300, opt);
if (enc.empty()) {
std::cerr << " × 编码失败: " << encoder.GetLastError() << std::endl;
return false;
}
double ratio = img.empty() ? 0.0 : static_cast<double>(img.size()) / enc.size();
std::cout << " 编码大小: " << (enc.size() / 1024.0) << " KB" << std::endl;
std::cout << " 压缩比: " << std::fixed << std::setprecision(2) << ratio << ":1" << std::endl;
// 保存文件
fs::path filepath = output_dir / (test_name + ".jb2");
std::ofstream f(filepath, std::ios::binary);
if (f.is_open()) {
f.write(reinterpret_cast<const char*>(enc.data()), enc.size());
f.close();
std::cout << " √ 已保存: " << filepath.string() << std::endl;
return true;
}
else {
std::cerr << " × 无法保存: " << filepath.string() << std::endl;
return false;
}
}
catch (const std::exception& e) {
std::cerr << " × 编码异常: " << e.what() << std::endl;
return false;
}
catch (...) {
std::cerr << " × 未知异常" << std::endl;
return false;
}
}
int main() {
// 设置输出路径
fs::path output_dir = "D:/jbig2_tests";
// 创建输出目录
try {
fs::create_directories(output_dir);
std::cout << "输出目录: " << fs::absolute(output_dir).string() << std::endl;
}
catch (const fs::filesystem_error& e) {
std::cerr << "创建目录失败: " << e.what() << std::endl;
return 1;
}
std::cout << "========================================" << std::endl;
std::cout << " JBIG2 编码综合测试" << std::endl;
std::cout << "========================================\n" << std::endl;
JBIG2Encoder encoder;
// 基础配置
JBIG2Encoder::Options base_opt;
base_opt.pdf_mode = false;
base_opt.verbose = false;
base_opt.threshold = 0.85f;
base_opt.weight = 0.5f;
base_opt.bw_threshold = 128;
// 统计信息
int total_tests = 0;
int passed_tests = 0;
//========================================
// 测试1: 不同尺寸的通用模式编码
//========================================
std::cout << "\n\n[测试1: 不同尺寸图像测试]" << std::endl;
std::vector<std::pair<int, int>> test_sizes = {
{1024, 1024}, // 1MP
{2048, 1536}, // 3MP
{2480, 3508}, // A4 @ 300dpi
{4096, 3072}, // 12MP
{4960, 7016} // A4 @ 600dpi
};
for (const auto& size : test_sizes) {
int w = size.first;
int h = size.second;
std::cout << "\n● 生成 " << w << "x" << h << " 几何形状图像..." << std::endl;
auto img = CreateRandomBinaryImage(w, h, 2); // 使用几何形状
auto opt = base_opt;
opt.mode = JBIG2Encoder::Mode::GENERIC;
std::string test_name = "generic_" + std::to_string(w) + "x" + std::to_string(h);
total_tests++;
if (TestSinglePage(encoder, img, w, h, test_name, opt, output_dir)) {
passed_tests++;
}
}
//========================================
// 测试2: 不同形状复杂度
//========================================
std::cout << "\n\n[测试2: 不同形状复杂度测试]" << std::endl;
int std_w = 2048;
int std_h = 1536;
std::vector<std::pair<std::string, std::vector<uint8_t>>> complexity_tests = {
{"random_noise", CreateRandomBinaryImage(std_w, std_h, 0)},
{"grid_pattern", CreateRandomBinaryImage(std_w, std_h, 1)},
{"geometric_shapes", CreateRandomBinaryImage(std_w, std_h, 2)},
{"circuit_board", CreateRandomBinaryImage(std_w, std_h, 3)},
{"mechanical_drawing", CreateRandomBinaryImage(std_w, std_h, 4)},
{"architecture_plan", CreateRandomBinaryImage(std_w, std_h, 5)},
{"map_pattern", CreateRandomBinaryImage(std_w, std_h, 6)},
{"technology_chart", CreateRandomBinaryImage(std_w, std_h, 7)},
{"gradient", CreateGradientImage(std_w, std_h)},
{"document", CreateDocumentImage(std_w, std_h)}
};
for (const auto& test : complexity_tests) {
std::cout << "\n● 测试: " << test.first << std::endl;
auto opt = base_opt;
opt.mode = JBIG2Encoder::Mode::GENERIC;
total_tests++;
if (TestSinglePage(encoder, test.second, std_w, std_h,
"shape_" + test.first, opt, output_dir)) {
passed_tests++;
}
}
//========================================
// 测试3: 符号模式测试(使用适合符号模式的图像)
//========================================
std::cout << "\n\n[测试3: 符号模式测试]" << std::endl;
// 测试1: 重复图案的小图(适合符号模式)
{
std::cout << "\n● 符号模式测试1: 重复图案 (256x256)" << std::endl;
int symbol_w = 256;
int symbol_h = 256;
auto symbol_img = CreateSymbolTestImage(symbol_w, symbol_h);
JBIG2Encoder::Options symbol_opt = base_opt;
symbol_opt.mode = JBIG2Encoder::Mode::SYMBOL;
symbol_opt.auto_threshold = true;
symbol_opt.use_hash = true;
symbol_opt.duplicate_line_removal = true;
//symbol_opt.max_symbol_size = 64;
//symbol_opt.min_symbol_size = 8;
total_tests++;
if (TestSinglePage(encoder, symbol_img, symbol_w, symbol_h,
"symbol_repeated_pattern", symbol_opt, output_dir)) {
passed_tests++;
}
}
// 测试2: 文档图像(文字模式)
{
std::cout << "\n● 符号模式测试2: 文档图像 (800x600)" << std::endl;
int doc_w = 800;
int doc_h = 600;
auto doc_img = CreateDocumentImage(doc_w, doc_h);
JBIG2Encoder::Options doc_opt = base_opt;
doc_opt.mode = JBIG2Encoder::Mode::SYMBOL;
doc_opt.auto_threshold = true;
doc_opt.use_hash = true;
doc_opt.duplicate_line_removal = true;
//doc_opt.max_symbol_size = 32;
//doc_opt.min_symbol_size = 4;
total_tests++;
if (TestSinglePage(encoder, doc_img, doc_w, doc_h,
"symbol_document", doc_opt, output_dir)) {
passed_tests++;
}
}
//========================================
// 测试4: 不同阈值设置
//========================================
std::cout << "\n\n[测试4: 不同阈值设置测试]" << std::endl;
auto test_img = CreateGeometricShapes(1024, 1024);
std::vector<float> thresholds = { 0.7f, 0.8f, 0.85f, 0.9f, 0.95f };
for (float thresh : thresholds) {
std::cout << "\n● 阈值: " << thresh << std::endl;
auto opt = base_opt;
opt.mode = JBIG2Encoder::Mode::GENERIC;
opt.threshold = thresh;
std::string test_name = "threshold_" + std::to_string(static_cast<int>(thresh * 100));
total_tests++;
if (TestSinglePage(encoder, test_img, 1024, 1024, test_name, opt, output_dir)) {
passed_tests++;
}
}
//========================================
// 测试5: 多页文档编码
//========================================
std::cout << "\n\n[测试5: 多页文档编码测试]" << std::endl;
std::vector<JBIG2Encoder::PageData> pages;
std::vector<std::string> page_names = { "几何形状", "电路板", "机械图", "建筑图", "地图" };
for (int i = 0; i < 5; ++i) {
int w = 2480;
int h = 3508;
std::cout << "\n● 生成页面 " << (i + 1) << ": " << page_names[i] << std::endl;
int pattern = i + 2; // 使用不同的形状模式
auto img_data = CreateRandomBinaryImage(w, h, pattern);
pages.emplace_back(img_data, w, h, 300, 1);
}
// 测试通用模式多页
std::cout << "\n● 通用模式多页编码..." << std::endl;
auto multi_generic = encoder.EncodeDocument(pages, base_opt);
if (!multi_generic.empty()) {
fs::path filepath = output_dir / "multi_page_shapes.jb2";
std::ofstream f(filepath, std::ios::binary);
if (f.is_open()) {
f.write(reinterpret_cast<const char*>(multi_generic.data()), multi_generic.size());
f.close();
std::cout << " √ 多页编码成功" << std::endl;
std::cout << " 总大小: " << (multi_generic.size() / 1024.0) << " KB" << std::endl;
std::cout << " 保存到: " << filepath.string() << std::endl;
passed_tests++;
}
else {
std::cerr << " × 无法保存文件: " << filepath.string() << std::endl;
}
}
else {
std::cerr << " × 多页编码失败: " << encoder.GetLastError() << std::endl;
}
total_tests++;
//========================================
// 测试完成
//========================================
std::cout << "\n\n========================================" << std::endl;
std::cout << " 所有测试完成!" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "测试统计:" << std::endl;
std::cout << " 总测试数: " << total_tests << std::endl;
std::cout << " 通过数: " << passed_tests << std::endl;
std::cout << " 失败数: " << (total_tests - passed_tests) << std::endl;
std::cout << "\n测试结果保存在: " << fs::absolute(output_dir).string() << std::endl;
std::cout << "按 Enter 键退出..." << std::endl;
std::cin.get();
return 0;
}
符号编码会卡死(暂时不清楚为什么)
c
#include "JBIG2Encoder.h"
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <filesystem>
#include <algorithm>
namespace fs = std::filesystem;
// 创建适合符号编码的文档图像(修复边界检查)
std::vector<uint8_t> CreateSymbolFriendlyImage(int w, int h) {
int stride = (w + 7) / 8;
size_t buf_size = static_cast<size_t>(stride) * h;
std::vector<uint8_t> buf(buf_size, 0xFF); // 白色背景
// 绘制简单的文字行,模拟文档
int line_height = 20;
int chars_per_line = w / 8;
for (int line = 0; line < h / line_height; ++line) {
int start_y = line * line_height + 5;
// 确保start_y不超出图像高度
if (start_y >= h) break;
// 绘制基线
for (int x = 50; x < w - 50; ++x) {
int byte_idx = start_y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
// 边界检查
if (byte_idx >= 0 && byte_idx < static_cast<int>(buf.size())) {
buf[byte_idx] &= ~(1 << bit_pos);
}
}
// 绘制字符点(模拟字母)
for (int char_idx = 0; char_idx < chars_per_line; ++char_idx) {
if (char_idx % 12 < 10) { // 每行10个字符
int start_x = 50 + char_idx * 8;
// 确保start_x不超出图像宽度
if (start_x >= w) continue;
// 绘制字符主体(垂直线)
for (int y = start_y - 12; y < start_y; ++y) {
if (y >= 0 && y < h) {
int byte_idx = y * stride + (start_x / 8);
int bit_pos = 7 - (start_x % 8);
// 边界检查
if (byte_idx >= 0 && byte_idx < static_cast<int>(buf.size())) {
buf[byte_idx] &= ~(1 << bit_pos);
}
}
}
// 绘制字符横线(模拟字母"A"的横线)
if (line % 2 == 0) {
int y = start_y - 6;
if (y < 0 || y >= h) continue;
for (int x = start_x; x < start_x + 5; ++x) {
if (x < 0 || x >= w) continue;
int byte_idx = y * stride + (x / 8);
int bit_pos = 7 - (x % 8);
// 边界检查
if (byte_idx >= 0 && byte_idx < static_cast<int>(buf.size())) {
buf[byte_idx] &= ~(1 << bit_pos);
}
}
}
}
}
}
return buf;
}
// 安全的图像尺寸计算函数
void ValidateImageParameters(int& width, int& height) {
// 确保尺寸有效
width = std::max(1, width);
height = std::max(1, height);
// 防止过大导致内存溢出
const int MAX_DIMENSION = 10000;
const int MAX_PIXELS = 10000000; // 1000万像素
width = std::min(width, MAX_DIMENSION);
height = std::min(height, MAX_DIMENSION);
// 检查像素总数
if (static_cast<long long>(width) * height > MAX_PIXELS) {
// 保持宽高比,调整尺寸
double aspect = static_cast<double>(width) / height;
height = static_cast<int>(sqrt(MAX_PIXELS / aspect));
width = static_cast<int>(height * aspect);
}
}
// 测试函数(添加参数验证)
bool TestSymbolEncoding(JBIG2Encoder& encoder,
const std::vector<uint8_t>& image_data,
int width, int height,
const std::string& test_name,
const JBIG2Encoder::Options& options,
const fs::path& output_dir) {
std::cout << "\n正在测试: " << test_name << std::endl;
// 验证输入参数
if (image_data.empty()) {
std::cout << " ❌ 错误: 输入图像数据为空" << std::endl;
return false;
}
if (width <= 0 || height <= 0) {
std::cout << " ❌ 错误: 无效的图像尺寸: " << width << "x" << height << std::endl;
return false;
}
int stride = (width + 7) / 8;
size_t expected_size = static_cast<size_t>(stride) * height;
if (image_data.size() != expected_size) {
std::cout << " ❌ 错误: 图像数据大小不匹配" << std::endl;
std::cout << " 预期: " << expected_size << " 字节" << std::endl;
std::cout << " 实际: " << image_data.size() << " 字节" << std::endl;
return false;
}
std::cout << "图像尺寸: " << width << "x" << height << std::endl;
std::cout << "步长: " << stride << " 字节" << std::endl;
std::cout << "数据大小: " << image_data.size() << " 字节" << std::endl;
try {
// 尝试编码
auto encoded_data = encoder.EncodePage(image_data, width, height, 300, options);
if (encoded_data.empty()) {
std::cout << " ❌ 编码失败: " << encoder.GetLastError() << std::endl;
return false;
}
// 计算压缩比
double original_size_kb = image_data.size() / 1024.0;
double encoded_size_kb = encoded_data.size() / 1024.0;
double compression_ratio = original_size_kb / std::max(encoded_size_kb, 0.001);
std::cout << " ✅ 编码成功!" << std::endl;
std::cout << " 原始大小: " << original_size_kb << " KB" << std::endl;
std::cout << " 编码后大小: " << encoded_size_kb << " KB" << std::endl;
std::cout << " 压缩比: " << std::fixed << compression_ratio << ":1" << std::endl;
// 保存文件
fs::path filepath = output_dir / (test_name + ".jb2");
std::ofstream file(filepath, std::ios::binary);
if (file.is_open()) {
file.write(reinterpret_cast<const char*>(encoded_data.data()),
static_cast<std::streamsize>(encoded_data.size()));
file.close();
std::cout << " 文件已保存: " << filepath.string() << std::endl;
return true;
}
else {
std::cout << " ❌ 无法保存文件" << std::endl;
return false;
}
}
catch (const std::exception& e) {
std::cout << " ❌ 异常: " << e.what() << std::endl;
return false;
}
catch (...) {
std::cout << " ❌ 未知异常" << std::endl;
return false;
}
}
int main() {
// 创建输出目录
fs::path output_dir = "D:/symbol_mode_tests";
try {
fs::create_directories(output_dir);
std::cout << "输出目录: " << fs::absolute(output_dir).string() << "\n" << std::endl;
}
catch (...) {
std::cout << "无法创建输出目录,使用当前目录\n" << std::endl;
output_dir = ".";
}
JBIG2Encoder encoder;
// 基本配置
JBIG2Encoder::Options base_options;
base_options.mode = JBIG2Encoder::Mode::SYMBOL;
base_options.pdf_mode = false;
base_options.verbose = true; // 开启详细输出以获取更多调试信息
base_options.threshold = 0.85f;
base_options.weight = 0.5f;
int total_tests = 0;
int passed_tests = 0;
std::cout << "==========================================" << std::endl;
std::cout << " JBIG2 符号模式编码测试" << std::endl;
std::cout << "==========================================\n" << std::endl;
// 测试1: 小尺寸文档图像
{
std::cout << "[测试1: 小尺寸文档图像]" << std::endl;
int width = 50;
int height = 50;
// 验证尺寸
ValidateImageParameters(width, height);
std::cout << "调整后尺寸: " << width << "x" << height << std::endl;
auto image = CreateSymbolFriendlyImage(width, height);
auto options = base_options;
options.auto_threshold = true;
options.use_hash = true;
options.duplicate_line_removal = true;
total_tests++;
if (TestSymbolEncoding(encoder, image, width, height,
"symbol_small_document", options, output_dir)) {
passed_tests++;
}
}
// 测试2: 标准文档图像(自动阈值,不使用hash)
{
std::cout << "\n[测试2: 标准文档图像(无hash)]" << std::endl;
int width = 800;
int height = 600;
// 验证尺寸
ValidateImageParameters(width, height);
std::cout << "调整后尺寸: " << width << "x" << height << std::endl;
auto image = CreateSymbolFriendlyImage(width, height);
auto options = base_options;
options.auto_threshold = true;
options.use_hash = false; // 测试不使用hash的版本
options.duplicate_line_removal = true;
total_tests++;
if (TestSymbolEncoding(encoder, image, width, height,
"symbol_standard_nohash", options, output_dir)) {
passed_tests++;
}
}
// 测试3: 手动阈值设置
{
std::cout << "\n[测试3: 手动阈值设置]" << std::endl;
int width = 600;
int height = 400;
// 验证尺寸
ValidateImageParameters(width, height);
std::cout << "调整后尺寸: " << width << "x" << height << std::endl;
auto image = CreateSymbolFriendlyImage(width, height);
auto options = base_options;
options.auto_threshold = false; // 手动设置阈值
options.threshold = 0.8f; // 较低的阈值
options.weight = 0.3f; // 较低的权重
total_tests++;
if (TestSymbolEncoding(encoder, image, width, height,
"symbol_manual_threshold", options, output_dir)) {
passed_tests++;
}
}
// 测试结果汇总
std::cout << "\n\n==========================================" << std::endl;
std::cout << " 测试结果汇总" << std::endl;
std::cout << "==========================================" << std::endl;
std::cout << "总测试数: " << total_tests << std::endl;
std::cout << "通过数: " << passed_tests << std::endl;
std::cout << "失败数: " << (total_tests - passed_tests) << std::endl;
std::cout << "\n输出目录: " << fs::absolute(output_dir).string() << std::endl;
// 清除输入缓冲区
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "\n按 Enter 键退出..." << std::endl;
std::cin.get();
return (passed_tests == total_tests) ? 0 : 1;
}