吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7504|回复: 121
收起左侧

[原创工具] 03-18更新,增加可调参数,增加多线程处理_图片黑白二值化的小工具,300多k,文件...

    [复制链接]
858983646 发表于 2025-3-12 21:24
本帖最后由 858983646 于 2025-3-18 19:16 编辑

不需要麻烦操作,拖过来就好,支持单个多个图片和单个文件夹简单图片漂白,免得打印出来一团黑,适合要求不高的场景,主要就是方便
速度很快,i5-6500测试了下1080p的图片,800张70多秒

03-18更新,增加了文件夹处理时多文件多线程处理,比原来快多了。单文件拖拽还是单图片拆分了多线程
参数也可以修改设置了,保存固定为了png格式,颜色索引模式,体积也小了好几倍

1741785259659.jpg 1741785259659_sauvola.jpg 1741785415346_sauvola.jpg

图片黑白二值化8.0(双击看说明)-11-0.05.zip

289.22 KB, 下载次数: 317, 下载积分: 吾爱币 -2 CB

可以修改参数了,加了文件夹转换多线程处理,速度快多了

免费评分

参与人数 29吾爱币 +33 热心值 +28 收起 理由
ysudo + 1 + 1 鼓励转贴优秀软件安全工具和文档!
zpzwz + 1 + 1 谢谢@Thanks!
heihuwudi + 1 + 1 谢谢@Thanks!
xk8899 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
ithanyun + 1 + 1 谢谢@Thanks!
墨与非 + 1 + 1 谢谢@Thanks!
mikezhql + 1 + 1 谢谢@Thanks!
viconly + 1 谢谢@Thanks!
RedGown + 1 谢谢@Thanks!
HAPPYCHICK + 1 + 1 谢谢@Thanks!
Ali2580 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
fm365 + 1 + 1 谢谢@Thanks!
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Yunsu + 1 + 1 win11可以用吗?
LakeBlue + 1 + 1 谢谢@Thanks!
lflxp + 1 + 1 谢谢@Thanks!
yanglinman + 1 + 1 谢谢@Thanks!
wangxp + 1 + 1 谢谢@Thanks!
lkwcz + 1 + 1 鼓励转贴优秀软件安全工具和文档!
daidai0516 + 1 我很赞同!
lsc987 + 1 + 1 我很赞同!
抱薪风雪雾 + 1 + 1 谢谢@Thanks!
rufengyj + 1 + 1 谢谢@Thanks!
yusen7983 + 1 + 1 谢谢@Thanks!
ehu4ever + 1 + 1 谢谢@Thanks!
shroer + 1 + 1 谢谢@Thanks!
schtg + 1 + 1 谢谢@Thanks!
柒點 + 1 + 1 谢谢@Thanks!
helh0275 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 858983646 发表于 2025-3-12 21:59
源码有需要的可以自己改改
[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;
}

 楼主| 858983646 发表于 2025-3-12 22:19
 楼主| 858983646 发表于 2025-3-12 21:58
caochunyuan 发表于 2025-3-12 21:30
试了一下,2MB的图片,会变成1MB的,总的来说还算可以,就是背景还有一些黑点。

参数不调整只能这样了,没办法,手动设置参数又太麻烦了没必要用这个东西
 楼主| 858983646 发表于 2025-3-12 22:07

虚拟机测试 win7 x86 sp1可以的,但是我之前单位老电脑win7确实不行,我也不知道什么原因
 楼主| 858983646 发表于 2025-3-12 22:51
Elaineliu 发表于 2025-3-12 22:47
为什么不用PM Lite呢,漂白软件有专门算法

那个我也下了,主要上班要求不高,不是打印出来很糊一坨就可以,这样更方便
 楼主| 858983646 发表于 2025-3-13 08:40
yangliu9420 发表于 2025-3-13 08:02
请教一下,黑白“二值化”是什么意思?

颜色变成纯黑和纯白两种啊
 楼主| 858983646 发表于 2025-3-13 08:46

颜色大于某个值就纯黑,否则纯白
 楼主| 858983646 发表于 2025-3-13 09:32
znw16899 发表于 2025-3-13 09:27
win10双击无法运行,是我方法不对吗

把图片或者文件夹拖到这个exe上,双击本来就不行
Elaineliu 发表于 2025-3-12 22:47
为什么不用PM Lite呢,漂白软件有专门算法

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
sunmoon007 + 1 + 1 我很赞同!

查看全部评分

caochunyuan 发表于 2025-3-12 21:30
试了一下,2MB的图片,会变成1MB的,总的来说还算可以,就是背景还有一些黑点。
BG8HVH 发表于 2025-3-12 22:02
win7不能运行
蓝箭 发表于 2025-3-12 22:10
运行了没反应。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-19 14:16

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表