博客网站大全制作二维码免费软件
博客网站大全,制作二维码免费软件,做外贸哪个英文网站好,秒收网站C语言文件操作结合OFA-Image-Caption#xff1a;批量处理图片并输出描述到文本文件
1. 引言
你有没有遇到过这样的场景#xff1f;电脑里存了几百上千张图片#xff0c;时间一长#xff0c;连自己都忘了每张图拍的是什么、从哪里来的。手动整理#xff1f;光是想想就头疼…C语言文件操作结合OFA-Image-Caption批量处理图片并输出描述到文本文件1. 引言你有没有遇到过这样的场景电脑里存了几百上千张图片时间一长连自己都忘了每张图拍的是什么、从哪里来的。手动整理光是想想就头疼。或者作为一个开发者你需要为大量的图片素材自动生成描述用于构建数据集、内容管理或者搜索索引。传统的做法可能是用Python写个脚本调用现成的库这当然没问题。但今天我想带你玩点不一样的我们用C语言来干这件事。为什么是C语言对于系统级开发、嵌入式环境或者对执行效率和资源控制有严格要求的场景C语言依然是无可替代的选择。它能让你直接操作文件系统、管理内存、进行网络通信整个过程透明且高效。这篇文章我就来分享一个实战案例用纯C语言编写一个程序它能自动扫描你指定文件夹里的所有图片调用一个名为OFA的AI模型它能“看懂”图片内容并生成文字描述最后把每张图片的路径和对应的描述整整齐齐地保存到一个文本文件里比如CSV格式方便你后续导入Excel或数据库进行处理。整个过程我们会涉及到C语言里几个核心且有趣的部分递归遍历目录、筛选图片文件、通过HTTP协议与AI服务“对话”、以及结构化的文件写入。如果你对系统编程和AI应用结合感兴趣那这篇内容应该能给你带来一些实用的启发。2. 核心思路与准备工作在动手写代码之前我们先理清整个程序要做什么以及需要准备些什么。2.1 我们要做什么程序的最终目标很明确输入一个文件夹路径输出一个包含了“图片路径”和“图片描述”两列信息的文本文件。具体来说程序需要完成以下几步找到所有图片从你给的起始文件夹开始往所有子文件夹里钻把后缀是.jpg、.png等的图片文件都找出来。请AI“看图说话”把找到的每一张图片发送给一个在服务器上运行好的OFA图像描述模型请它用一句话描述图片里有什么。保存结果把图片的完整路径和AI返回的描述文字配对记录下来写入到一个新的文件里。2.2 需要准备什么工欲善其事必先利其器。我们主要需要两方面的准备1. 开发环境与C语言库编译器一个能用的C编译器比如GCCLinux/macOS或者MinGWWindows。目录遍历我们需要用到操作系统的接口来遍历文件夹。在Linux/macOS上我们会用dirent.h在Windows上则是windows.h里的相关函数。为了简化本文将以类Unix系统的接口为例进行说明。网络请求为了调用AI模型的API我们需要用C语言发送HTTP请求。这里我们会使用一个非常流行的、名为libcurl的客户端URL传输库。它功能强大能帮我们处理复杂的HTTP通信细节。2. 可访问的OFA图像描述服务这是整个项目的“大脑”。OFA是一个多模态模型其中的图像描述Image Caption能力很强。你需要有一个正在运行的OFA服务它提供了一个HTTP API接口。这个服务可以是你自己在本地或服务器上部署的也可以是某个提供给你的测试服务。关键是你需要知道它的访问地址URL例如http://127.0.0.1:8080/caption。通常你需要向这个地址发送一个包含图片的POST请求然后它会返回一段JSON格式的文字描述。思路清晰了工具也备好了接下来我们就一步步用代码把它们实现出来。3. 第一步用C语言遍历目录与识别图片我们的首要任务是让程序能自己“走遍”文件夹的每个角落并把图片文件挑出来。这就像是派一个机器人去档案室把所有带照片标记的文件夹找出来。3.1 递归遍历目录在C语言中opendir、readdir、closedir这几个函数是我们的好帮手。递归的思想很简单进入一个目录列出里面所有项目如果是文件就判断是不是图片如果是子目录就再次调用自己进入这个子目录继续寻找。下面是一个递归遍历目录的核心函数框架#include stdio.h #include dirent.h #include string.h #include sys/stat.h // 判断文件是否为图片根据后缀名 int is_image_file(const char *filename) { const char *ext strrchr(filename, .); // 找到最后一个点号 if (ext NULL) return 0; // 没有后缀名 // 将后缀名转换为小写再比较可选更健壮 char lower_ext[10]; strcpy(lower_ext, ext); for(int i0; lower_ext[i]; i) lower_ext[i] tolower(lower_ext[i]); // 检查常见图片后缀 if (strcmp(lower_ext, .jpg) 0 || strcmp(lower_ext, .jpeg) 0 || strcmp(lower_ext, .png) 0 || strcmp(lower_ext, .bmp) 0 || strcmp(lower_ext, .gif) 0) { return 1; } return 0; } // 递归遍历目录的核心函数 void scan_directory(const char *base_path, void (*process_file)(const char*)) { DIR *dir; struct dirent *entry; struct stat path_stat; char full_path[1024]; if ((dir opendir(base_path)) NULL) { perror(无法打开目录); return; } while ((entry readdir(dir)) ! NULL) { // 跳过当前目录(.)和上级目录(..) if (strcmp(entry-d_name, .) 0 || strcmp(entry-d_name, ..) 0) continue; // 构建完整的文件/目录路径 snprintf(full_path, sizeof(full_path), %s/%s, base_path, entry-d_name); // 获取文件信息判断是文件还是目录 if (stat(full_path, path_stat) ! 0) { perror(获取文件状态失败); continue; } if (S_ISDIR(path_stat.st_mode)) { // 如果是目录递归调用自身 scan_directory(full_path, process_file); } else if (S_ISREG(path_stat.st_mode)) { // 如果是普通文件且是图片则交给处理函数 if (is_image_file(entry-d_name)) { process_file(full_path); } } } closedir(dir); }这个scan_directory函数是整个文件搜索的引擎。它接受一个起始路径和一个函数指针process_file。每当找到一个图片文件它就会调用这个process_file函数并把图片的完整路径传给它。这样设计的好处是搜索逻辑和处理逻辑分开了非常清晰。3.2 设计图片处理流程现在图片文件的路径已经被找到了。我们需要一个函数来处理它也就是我们上面提到的process_file。这个函数未来要干三件大事读取图片文件的数据。把图片数据发给AI服务拿到描述文本。把路径和描述一起保存下来。为了逻辑清晰我们先搭好架子把网络请求和结果保存留到后面实现。这里我们先让这个处理函数打印出找到的图片路径确认我们的目录遍历是工作的。// 图片处理函数第一阶段仅打印路径 void process_image_file(const char *filepath) { printf(找到图片: %s\n, filepath); // 后续将在这里添加读取图片、调用API、保存结果 } int main() { const char *start_path ./pictures; // 你可以修改为你的图片目录 printf(开始扫描目录: %s\n, start_path); scan_directory(start_path, process_image_file); printf(扫描完成。\n); return 0; }把上面的代码编译运行一下如果能看到它正确地列出你./pictures文件夹下所有图片的路径那么恭喜你最基础的“文件搜寻”模块已经成功了。4. 第二步集成libcurl调用OFA API找到了图片下一步就是让AI“看”到它们。我们需要通过HTTP协议把图片数据发送给OFA服务。libcurl库将在这里大显身手。4.1 使用libcurl发送图片与接收描述假设我们的OFA服务提供了一个简单的API向某个URL例如http://localhost:8000/caption发送一个POST请求请求体是图片的二进制数据服务器就会返回一段JSON其中包含了描述文字。使用libcurl的基本步骤是初始化、设置选项、执行请求、清理。我们需要设置的关键选项包括请求URL、请求方法为POST、添加图片数据作为请求体、以及设置一个回调函数来接收服务器返回的数据。下面是一个封装好的函数它接受图片文件路径调用API并返回AI生成的描述字符串。#include curl/curl.h #include stdlib.h // 用于存储HTTP响应数据的内存结构 struct MemoryStruct { char *memory; size_t size; }; // libcurl接收数据的回调函数 static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize size * nmemb; struct MemoryStruct *mem (struct MemoryStruct *)userp; char *ptr realloc(mem-memory, mem-size realsize 1); if(ptr NULL) { printf(错误无法分配内存用于存储响应数据。\n); return 0; } mem-memory ptr; memcpy((mem-memory[mem-size]), contents, realsize); mem-size realsize; mem-memory[mem-size] 0; // 添加字符串结束符 return realsize; } // 调用OFA API获取图片描述的核心函数 char* get_image_caption(const char *image_path, const char *api_url) { CURL *curl; CURLcode res; struct MemoryStruct chunk; char *caption NULL; // 初始化响应数据存储 chunk.memory malloc(1); chunk.size 0; // 初始化libcurl curl_global_init(CURL_GLOBAL_DEFAULT); curl curl_easy_init(); if(curl) { FILE *fp fopen(image_path, rb); if (!fp) { fprintf(stderr, 无法打开图片文件: %s\n, image_path); free(chunk.memory); curl_easy_cleanup(curl); return NULL; } // 获取文件大小 fseek(fp, 0L, SEEK_END); long fsize ftell(fp); fseek(fp, 0L, SEEK_SET); // 设置libcurl选项 curl_easy_setopt(curl, CURLOPT_URL, api_url); curl_easy_setopt(curl, CURLOPT_POST, 1L); // 设置POST数据图片文件 curl_easy_setopt(curl, CURLOPT_READDATA, fp); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, fsize); // 设置接收响应数据的回调函数 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk); // 执行请求 res curl_easy_perform(curl); // 检查请求是否成功 if(res ! CURLE_OK) { fprintf(stderr, curl_easy_perform() 失败: %s\n, curl_easy_strerror(res)); } else { // 假设API返回的是纯JSON例如 {caption: a dog sitting on the grass} // 这里需要根据你实际API的返回格式进行解析 // 以下是一个简单的示例假设返回的JSON中caption字段的值就是描述 // 在实际使用中你应该使用一个JSON解析库如 cJSON const char *prefix \caption\:\; char *start strstr(chunk.memory, prefix); if (start) { start strlen(prefix); char *end strchr(start, \); if (end) { size_t len end - start; caption malloc(len 1); strncpy(caption, start, len); caption[len] \0; } } if (!caption) { // 如果解析失败可能返回的不是预期JSON这里简单截取一部分作为描述 caption strndup(chunk.memory, 100); } } // 清理 fclose(fp); curl_easy_cleanup(curl); } curl_global_cleanup(); free(chunk.memory); return caption; // 调用者需要负责释放这个内存 }重要提示上面的JSON解析部分非常简陋仅作演示。在实际项目中OFA API返回的JSON结构可能更复杂。强烈建议集成一个轻量级的JSON解析库如cJSON来可靠地提取caption字段。4.2 完善图片处理函数现在我们可以升级之前的process_image_file函数了让它真正去调用API并获取描述。// 定义OFA服务的API地址 #define OFA_API_URL http://127.0.0.1:8080/caption // 请替换为你的实际地址 // 升级后的图片处理函数 void process_image_file(const char *filepath) { printf(处理图片: %s\n, filepath); char *caption get_image_caption(filepath, OFA_API_URL); if (caption) { printf( 描述: %s\n, caption); // 这里暂时打印下一步我们会将其保存到文件 // TODO: 保存 (filepath, caption) 到结果文件 free(caption); // 释放get_image_caption分配的内存 } else { printf( 错误无法获取图片描述。\n); } }编译这个程序时别忘了链接libcurl库gcc -o image_crawler your_program.c -lcurl运行程序如果一切配置正确OFA服务已启动且地址无误你应该能看到程序一边找图一边打印出AI生成的描述了。5. 第三步将结果写入结构化文本文件最后一步也是成果落地的关键把收集到的信息持久化保存。我们需要一个结构化的格式CSV逗号分隔值是个好选择因为它简单且能被Excel、数据库等众多工具直接识别。5.1 设计输出格式与文件写入我们计划生成一个image_descriptions.csv文件。第一行是表头后续每一行都是一条记录包含图片路径和描述。由于描述中可能包含逗号、引号或换行符为了确保CSV格式正确我们最好用双引号将每个字段包裹起来。#include stdlib.h // 全局结果文件指针 FILE *result_file NULL; // 初始化结果文件写入表头 int init_result_file(const char *filename) { result_file fopen(filename, w); if (result_file NULL) { perror(无法创建结果文件); return 0; // 失败 } // 写入CSV表头 fprintf(result_file, \image_path\,\description\\n); fflush(result_file); // 立即写入避免缓冲 return 1; // 成功 } // 将一条记录图片路径和描述写入CSV文件 void write_result_to_file(const char *filepath, const char *caption) { if (result_file NULL) return; // 转义字段中的双引号CSV规范用两个双引号表示一个双引号 // 这里简化处理假设描述中不包含双引号。实际应用中可能需要更复杂的转义。 fprintf(result_file, \%s\,\%s\\n, filepath, caption); fflush(result_file); // 每次写入后刷新确保数据及时保存 } // 最终版本的图片处理函数 void process_image_file_and_save(const char *filepath) { printf(处理图片: %s\n, filepath); char *caption get_image_caption(filepath, OFA_API_URL); if (caption) { printf( 描述: %s\n, caption); write_result_to_file(filepath, caption); free(caption); } else { printf( 错误无法获取图片描述。\n); // 即使失败也可以选择写入一条错误记录 write_result_to_file(filepath, [ERROR: Failed to get caption]); } } // 关闭结果文件 void close_result_file() { if (result_file) { fclose(result_file); result_file NULL; } }5.2 整合主程序逻辑现在我们把所有模块像拼图一样组合起来形成一个完整的、可工作的程序。int main(int argc, char *argv[]) { const char *start_path; const char *output_file image_descriptions.csv; // 简单的参数处理允许用户通过命令行参数指定目录和输出文件 if (argc 2) { printf(用法: %s 图片目录路径 [输出CSV文件路径]\n, argv[0]); printf(示例: %s ./my_photos ./output.csv\n, argv[0]); start_path ./pictures; // 默认目录 } else { start_path argv[1]; } if (argc 3) { output_file argv[2]; } printf(图片描述批量处理工具\n); printf(扫描目录: %s\n, start_path); printf(输出文件: %s\n, output_file); // 1. 初始化结果文件 if (!init_result_file(output_file)) { fprintf(stderr, 程序初始化失败无法创建输出文件。\n); return 1; } // 2. 开始递归扫描并处理图片 printf(开始处理...\n); scan_directory(start_path, process_image_file_and_save); printf(处理完成。\n); // 3. 关闭文件清理资源 close_result_file(); return 0; }将以上所有代码段整合到一个.c文件中确保包含了所有必要的头文件并使用正确的命令编译记得链接libcurl和可能需要链接的cJSON库。运行程序指定你的图片文件夹稍等片刻一个崭新的image_descriptions.csv文件就会生成在你指定的位置。6. 总结回顾一下我们完成了一件挺酷的事情用C语言这个相对底层的工具完成了一个结合文件操作、网络通信和AI服务的应用。我们从递归遍历目录开始学会了如何让程序自己探索文件夹结构然后集成libcurl实现了与远程AI服务的HTTP对话最后我们将结果规整地写入CSV文件让数据变得可用。这个过程展示了C语言在系统级任务中的灵活性。虽然相比Python等脚本语言C需要你更多地关注内存、文件指针和网络细节但这种控制力也带来了效率和确定性的优势特别适合集成到更大的系统、后台服务或资源受限的环境中。当然这个示例程序还有很多可以打磨的地方比如更健壮的JSON解析、更完善的错误处理、支持更多图片格式、添加处理进度显示、甚至引入多线程来加速处理等等。但这正是一个有趣项目的起点。你可以基于这个框架根据自己的需求去扩展和优化。希望这个案例能为你打开一扇窗看到将传统的系统编程与现代AI能力结合的可能性。动手试试把它运行起来看看你的电脑里的图片都会被AI如何描述吧。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。