#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);
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
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;
}