[C] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <pthread.h>
#include <dirent.h> // 用于遍历文件夹
#include <sys/stat.h> // 用于检查文件是否存在
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
// 添加平台相关的头文件
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
typedef struct {
int width;
int height;
unsigned char *data;
} GrayscaleImage;
typedef struct {
double *integral;
double *integral_sq;
int w;
int h;
} IntegralData;
IntegralData* create_integral(GrayscaleImage *img) {
IntegralData *id = malloc(sizeof(IntegralData));
id->w = img->width;
id->h = img->height;
id->integral = calloc(id->w * id->h, sizeof(double));
id->integral_sq = calloc(id->w * id->h, sizeof(double));
double row_sum = 0, row_sum_sq = 0;
for(int x=0; x<id->w; x++){
double val = img->data[x];
row_sum += val;
row_sum_sq += val*val;
id->integral[x] = (x>0) ? id->integral[x-1] + val : val;
id->integral_sq[x] = (x>0) ? id->integral_sq[x-1] + val*val : val*val;
}
for(int y=1; y<id->h; y++){
row_sum = 0;
row_sum_sq = 0;
for(int x=0; x<id->w; x++){
int idx = y*id->w + x;
double val = img->data[idx];
row_sum += val;
row_sum_sq += val*val;
double up = id->integral[(y-1)*id->w + x];
id->integral[idx] = up + row_sum;
id->integral_sq[idx] = id->integral_sq[(y-1)*id->w + x] + row_sum_sq;
}
}
return id;
}
void get_local_stats(IntegralData *id, int x, int y, int radius,
double *mean, double *stddev)
{
int x1 = fmax(0, x - radius);
int y1 = fmax(0, y - radius);
int x2 = fmin(id->w-1, x + radius);
int y2 = fmin(id->h-1, y + radius);
int area = (x2-x1+1)*(y2-y1+1);
if(area == 0) {
*mean = 0;
*stddev = 0;
return;
}
double sum = id->integral[y2*id->w + x2];
if(x1 > 0) sum -= id->integral[y2*id->w + x1-1];
if(y1 > 0) sum -= id->integral[(y1-1)*id->w + x2];
if(x1>0 && y1>0) sum += id->integral[(y1-1)*id->w + x1-1];
double sum_sq = id->integral_sq[y2*id->w + x2];
if(x1 > 0) sum_sq -= id->integral_sq[y2*id->w + x1-1];
if(y1 > 0) sum_sq -= id->integral_sq[(y1-1)*id->w + x2];
if(x1>0 && y1>0) sum_sq += id->integral_sq[(y1-1)*id->w + x1-1];
*mean = sum / area;
double variance = (sum_sq / area) - (*mean * *mean);
*stddev = sqrt(fmax(variance, 0.0));
}
typedef struct {
GrayscaleImage *img;
IntegralData *id;
int start_row;
int end_row;
int radius;
double k;
double R;
} ThreadArgs;
void* thread_function(void* args) {
ThreadArgs *targs = (ThreadArgs*)args;
GrayscaleImage *img = targs->img;
IntegralData *id = targs->id;
int radius = targs->radius;
double k = targs->k;
double R = targs->R;
for(int y = targs->start_row; y < targs->end_row; y++) {
for(int x = 0; x < img->width; x++) {
double mean, stddev;
get_local_stats(id, x, y, radius, &mean, &stddev);
double threshold = mean * (1.0 + k * (stddev/R - 1.0));
img->data[y*img->width + x] = (img->data[y*img->width + x] > threshold) ? 255 : 0;
}
}
return NULL;
}
void sauvola_binarize(GrayscaleImage *img, int window_size, double k) {
int radius = window_size / 2;
IntegralData *id = create_integral(img);
double R = 0;
for(int i=0; i<img->width*img->height; i++)
if(img->data[i] > R) R = img->data[i];
R = fmax(R, 1.0);
// 自动获取CPU核心数
int num_threads = 4; // 默认值
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
num_threads = sysInfo.dwNumberOfProcessors;
#else
long nproc = sysconf(_SC_NPROCESSORS_ONLN);
if (nproc > 0) {
num_threads = (int)nproc;
}
#endif
// 确保至少1个线程
num_threads = num_threads > 0 ? num_threads : 1;
pthread_t *threads = malloc(num_threads * sizeof(pthread_t));
ThreadArgs *args = malloc(num_threads * sizeof(ThreadArgs));
int rows_per_thread = (img->height + num_threads - 1) / num_threads;
for(int i = 0; i < num_threads; i++) {
args[i].img = img;
args[i].id = id;
args[i].radius = radius;
args[i].k = k;
args[i].R = R;
args[i].start_row = i * rows_per_thread;
args[i].end_row = fmin((i + 1) * rows_per_thread, img->height);
pthread_create(&threads[i], NULL, thread_function, &args[i]);
}
for(int i = 0; i < num_threads; i++) {
pthread_join(threads[i], NULL);
}
free(threads);
free(args);
free(id->integral);
free(id->integral_sq);
free(id);
}
// 检查文件是否是图片
int is_image_file(const char *filename) {
const char *ext = strrchr(filename, '.');
if (!ext) return 0;
if (strcmp(ext, ".png") == 0 || strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0 || strcmp(ext, ".bmp") == 0) {
return 1;
}
return 0;
}
// 处理单个图片文件
void process_image_file(const char *input_path) {
int w, h, ch;
unsigned char *data = stbi_load(input_path, &w, &h, &ch, 1);
if (!data) {
fprintf(stderr, "无法加载图片: %s\n", input_path);
return;
}
GrayscaleImage img = {w, h, data};
sauvola_binarize(&img, 11, 0.05);
// 构造输出文件名
char output_path[256];
const char *dot = strrchr(input_path, '.');
if (dot) {
snprintf(output_path, sizeof(output_path), "%.*s_sauvola%s", (int)(dot - input_path), input_path, dot);
} else {
snprintf(output_path, sizeof(output_path), "%s_sauvola", input_path);
}
stbi_write_png(output_path, w, h, 1, img.data, w);
stbi_image_free(data);
printf("处理完成: %s -> %s\n", input_path, output_path);
}
// 处理文件夹
void process_folder(const char *folder_path) {
DIR *dir = opendir(folder_path);
if (!dir) {
fprintf(stderr, "无法打开文件夹: %s\n", folder_path);
return;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// 检查是否是文件
char file_path[256];
snprintf(file_path, sizeof(file_path), "%s/%s", folder_path, entry->d_name);
struct stat path_stat;
if (stat(file_path, &path_stat) == 0) {
if (S_ISREG(path_stat.st_mode)) { // 检查是否是普通文件
if (is_image_file(entry->d_name)) {
process_image_file(file_path);
}
}
}
}
closedir(dir);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("用法: 将图片或文件夹拖放到此exe上\n");
return 1;
}
for (int i = 1; i < argc; i++) {
const char *path = argv[i];
// 检查是否是文件夹
struct stat path_stat;
if (stat(path, &path_stat) == 0) {
if (S_ISDIR(path_stat.st_mode)) {
process_folder(path);
} else {
process_image_file(path);
}
} else {
fprintf(stderr, "无效的路径: %s\n", path);
}
}
return 0;
}