C语言实现BMP图像处理(缩放)

本文介绍使用C语言实现BMP图像的缩放功能,包括直接缩放、最近邻插值法和双线性插值法。通过实验对比不同方法的效果,详细解释了每种方法的实现过程及优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对BMP图像用C语言实现图像的缩放。缩放的倍数通过输入设定


#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

int main(int argc, char ** argv)
{
	FILE *fp = fopen("./01.bmp","rb");
	if (fp == 0)
		return 0;
	BITMAPFILEHEADER fileHead;
	fread(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
	BITMAPINFOHEADER infoHead;
	fread(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);
	int width = infoHead.biWidth;
	int height = infoHead.biHeight;
	int biCount = infoHead.biBitCount;

	RGBQUAD *pColorTable;
	int pColorTableSize = 0;

	pColorTable = new RGBQUAD[256];
	fread(pColorTable, sizeof(RGBQUAD), 256, fp);
	pColorTableSize = 1024;

	unsigned char *pBmpBuf;
	int lineByte = (width*biCount / 8 + 3) / 4 * 4;
	pBmpBuf = new unsigned char[lineByte*height];
	fread(pBmpBuf, lineByte*height, 1, fp);
	fclose(fp);

	printf("请输入长和宽需要缩放的倍数:");
	float lx, ly;
	scanf("%f%f", &lx, &ly);
	int dstWidth = round(double(lx*width));
	int dstHeight = round(double(ly*height));
	int lineByte2 = (dstWidth*biCount / 8 + 3) / 4 * 4;
	
	unsigned char*pBmpBuf2;
	pBmpBuf2 = new unsigned char[lineByte2*dstHeight];
	for (int i = 0; i < dstHeight; ++i){
		for (int j = 0; j < dstWidth; ++j){
			unsigned char *p;
			p = (unsigned char *)(pBmpBuf2 + lineByte2*i + j);
			(*p) = 255;
		}
	}
	int x = 0;
	int y = 0;
	for (int i = 0; i < height; ++i){
		for (int j = 0; j < width; ++j){
			unsigned char *p1, *p2;
			x = round(double(lx*j));
			y = round(double(ly*i));
			p1 = (unsigned char *)(pBmpBuf + i*lineByte + j);
			p2 = (unsigned char *)(pBmpBuf2 + y*lineByte2 + x);
			(*p2) = (*p1);
		}
	}

	FILE *fpo = fopen("./shrinkBmp.bmp", "wb");
	if (fpo == 0)
		return 0;
	BITMAPFILEHEADER dstFileHead;
	dstFileHead.bfOffBits = 14 + 40 + pColorTableSize;
	dstFileHead.bfReserved1 = 0;
	dstFileHead.bfReserved2 = 0;
	dstFileHead.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+pColorTableSize + lineByte2*dstHeight;
	dstFileHead.bfType = 0x4D42;
	fwrite(&dstFileHead, sizeof(dstFileHead), 1, fpo);

	BITMAPINFOHEADER dstInfoHead;
	dstInfoHead.biBitCount = biCount;
	dstInfoHead.biClrImportant = 0;
	dstInfoHead.biClrUsed = 0;
	dstInfoHead.biCompression = 0;
	dstInfoHead.biHeight = dstHeight;
	dstInfoHead.biPlanes = 1;
	dstInfoHead.biSize = 40;
	dstInfoHead.biSizeImage = lineByte2*dstHeight;
	dstInfoHead.biWidth = dstWidth;
	dstInfoHead.biXPelsPerMeter = 0;
	dstInfoHead.biYPelsPerMeter = 0;
	fwrite(&dstInfoHead, sizeof(BITMAPINFOHEADER), 1, fpo);

	fwrite(pColorTable, sizeof(RGBQUAD), 256, fpo);
	fwrite(pBmpBuf2, lineByte2*dstHeight, 1, fp);
	fclose(fpo);

	system("pause");
	return 0;
}

实验结果:

              原图                     宽1.5倍,高0.5倍       宽0.5倍 高1.5倍            宽5倍 高2倍                  

      

变换后的图像都有一个共同的特点,就是图像有栅格,出现的原因是在缩放坐标取值时采用简单的取整,解决办法:采用插值解决

方法一:最近邻插值法,其实就是四舍五入

	for (int i = 0; i < dstHeight; ++i){
		for (int j = 0; j < dstWidth; ++j){
			unsigned char *p1, *p2;
			x = round(double(1/lx*j));
			y = round(double(1/ly*i));
			p1 = (unsigned char *)(pBmpBuf2 + i*lineByte2 + j);
			p2 = (unsigned char *)(pBmpBuf + y*lineByte + x);
			(*p1) = (*p2);
		}
	}

实验结果:

方法倍数:长1.5 宽1.5

 

实验结果分析:能过保留细节部分,但边缘存在严重的锯齿现象。

方法二:双线性插值

C代码实现:

int x1, x2, y1, y2, Fq11, Fq12, Fq21, Fq22;
	double x, y, r1, Fr1, Fr2, Fp;
	for (int i = 0; i < dstHeight; ++i){
		for (int j = 0; j < dstWidth; ++j){
			unsigned char *p1, *p2;
			x = 1 / lx*j; // 原图像坐标
			y = 1 / ly*i;
			// 四个坐标值
			x1 = floor(x); x2 = x1 + 1;// 取整ceil向上,floor向下
			y1 = floor(y); y2 = y1 + 1;
			// 四个坐标对应的灰度值
			Fq11 = *(pBmpBuf + y1*lineByte + x1);
			Fq12 = *(pBmpBuf + y2*lineByte + x1);
			Fq21 = *(pBmpBuf + y1*lineByte + x2);
			Fq22 = *(pBmpBuf + y2*lineByte + x2);
			// x方向插值和y方向插值
			Fr1 = 0; Fr2 = 0;

			Fr1 = (x2 - x) / (x2 - x1)*Fq11 + (x - x1) / (x2 - x1)*Fq21;
			Fr2 = (x2 - x) / (x2 - x1)*Fq12 + (x - x1) / (x2 - x1)*Fq22;
			Fp = (y2 - y) / (y2 - y1)*Fr1 + (y - y1) / (y2 - y1)*Fr2;
			// 新图像灰度值赋值
			if (Fp >= 0 && Fp <= 255){
				p1 = (unsigned char *)(pBmpBuf2 + i*lineByte2 + j); // 新图像
				(*p1) = round(Fp);
			}
		}
	}

实验结果:

长宽都放大1.5倍

后续持续更新用C语言实现图像处理算法,敬请期待,欢迎关注。

<think>我们计划使用一个C语言的PDF解析库。由于PDF格式复杂,通常建议使用现有的库,如libpoppler或mupdf。 但是,请注意,这些库通常是用C++编写的,不过它们有C的接口或者我们可以用C来调用C++库(需要封装)。 这里我们选择使用mupdf,因为它有一个相对简单的API,并且是轻量级的。 步骤: 1. 安装mupdf库 2. 配置VSCode的C/C++环境,包含mupdf的头文件和链接库 3. 编写C代码打开PDF文件并提取文本 由于在VSCode中开发,我们需要先安装mupdf开发包。 在Ubuntu系统中,可以使用以下命令安装: ```bash sudo apt-get update sudo apt-get install libmupdf-dev ``` 然后,我们创建一个C文件,使用mupdf来读取PDF内容。 注意:mupdf的主要API是C++,但它也提供了C的API(在mupdf/fitz.h等头文件中)。我们可以使用C接口。 以下是一个简单的示例代码,用于打开PDF文件并打印每一页的文本: 注意:由于mupdf的API较为复杂,这里仅提供一个基本示例。在实际使用中,需要处理错误和内存管理。 代码示例: ```c #include <stdio.h> #include <mupdf/fitz.h> int main(int argc, char **argv) { const char *filename = "test.pdf"; // 替换为你的PDF文件路径 fz_context *ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); if (!ctx) { fprintf(stderr, "Failed to create context.\n"); return 1; } // 注册默认字体和编码 fz_try(ctx) { fz_register_document_handlers(ctx); } fz_catch(ctx) { fprintf(stderr, "Failed to register document handlers: %s\n", fz_caught_message(ctx)); fz_drop_context(ctx); return 1; } // 打开文档 fz_document *doc = NULL; fz_try(ctx) { doc = fz_open_document(ctx, filename); } fz_catch(ctx) { fprintf(stderr, "Failed to open document: %s\n", fz_caught_message(ctx)); fz_drop_context(ctx); return 1; } // 获取页数 int page_count = fz_count_pages(ctx, doc); printf("Page count: %d\n", page_count); // 遍历每一页 for (int i = 0; i < page_count; i++) { fz_page *page = NULL; fz_try(ctx) { page = fz_load_page(ctx, doc, i); } fz_catch(ctx) { fprintf(stderr, "Failed to load page %d: %s\n", i+1, fz_caught_message(ctx)); continue; } // 创建一个文本页 fz_stext_page *text_page = NULL; fz_stext_options options = { FZ_STEXT_PRESERVE_IMAGES }; // 可以设置选项,这里我们保留默认 fz_try(ctx) { text_page = fz_new_stext_page_from_page(ctx, page, &options); } fz_catch(ctx) { fprintf(stderr, "Failed to extract text from page %d: %s\n", i+1, fz_caught_message(ctx)); fz_drop_page(ctx, page); continue; } // 获取文本 fz_stext_block *block; for (block = text_page->first_block; block; block = block->next) { if (block->type == FZ_STEXT_BLOCK_TEXT) { fz_stext_line *line; for (line = block->u.text.first_line; line; line = line->next) { fz_stext_char *ch; for (ch = line->first_char; ch; ch = ch->next) { printf("%c", ch->c); } printf("\n"); } } } // 释放资源 fz_drop_stext_page(ctx, text_page); fz_drop_page(ctx, page); } // 释放文档和上下文 fz_drop_document(ctx, doc); fz_drop_context(ctx); return 0; } ``` 编译命令(在VSCode的终端中,假设文件名为pdf_reader.c): ```bash gcc -o pdf_reader pdf_reader.c -lmupdf -lmupdf-third ``` 注意:根据系统安装的mupdf版本,可能需要链接其他库,如`-lfreetype`、`-ljbig2dec`、`-ljpeg`、`-lopenjp2`、`-lz`等。如果编译时提示缺少这些库,请安装它们并添加到链接选项中。 在VSCode中配置任务(tasks.json)以方便编译: 我们可以配置一个任务来编译这个程序。按Ctrl+Shift+P,输入“任务:配置任务”,然后选择“使用模板创建tasks.json文件”->“Others”,然后编辑tasks.json文件,添加: ```json { "version": "2.0.0", "tasks": [ { "label": "build pdf reader", "type": "shell", "command": "gcc", "args": [ "-g", "-o", "${fileDirname}/${fileBasenameNoExtension}", "${file}", "-lmupdf", "-lmupdf-third" ], "group": { "kind": "build", "isDefault": true } } ] } ``` 然后按Ctrl+Shift+B即可编译。 注意:上面的代码示例仅用于演示,实际使用中需要更完善的错误处理。另外,mupdf的API可能会随着版本变化而改变,请参考官方文档。 引用说明: [^1]: 关于VSCode的快捷键和基本操作,可以参考引用[1]。 [^2]: 关于图形库LVGL的介绍,虽然与PDF阅读无关,但展示了库的使用方式,可类比参考。 [^3]: Boost库的文档风格和代码质量值得借鉴,但这里使用的是mupdf。 [^4]: Markdown和Graphviz的关注点分离思想,我们在使用库时也应关注功能而非底层细节。 相关问题: §§相关问题§§ 1. 如何解决在编译链接mupdf库时出现的未定义引用错误? 2. 除了mupdf,还有哪些C/C++库可以用于解析PDF文件? 3. 如何提取PDF文件中的图片或其他非文本内容? 4. 如何处理加密的PDF文件?</think>### 在VSCode中使用C语言读取PDF文件内容 #### 方法概述 在C语言中读取PDF需要借助第三方库,因为PDF格式复杂(包含文本、图像、字体、压缩等)。推荐使用 **MuPDF 库**(轻量级开源PDF解析库),具体步骤如下: --- #### 步骤1:安装依赖库 在终端执行以下命令安装MuPDF开发包(Ubuntu示例): ```bash sudo apt-get update sudo apt-get install libmupdf-dev # 安装MuPDF开发库[^1] ``` --- #### 步骤2:配置VSCode环境 1. **创建C文件** 新建 `pdf_reader.c` 文件 2. **配置编译器** 修改 `.vscode/tasks.json` 添加编译参数: ```json { "version": "2.0.0", "tasks": [ { "label": "build", "type": "shell", "command": "gcc", "args": [ "-o", "pdf_reader", "pdf_reader.c", "-lmupdf", // 链接MuPDF库 "-lfreetype", // 字体渲染支持 "-ljbig2dec", // 图像解码支持 "-I/usr/include/mupdf" // 头文件路径 ], "group": "build" } ] } ``` --- #### 步骤3:代码示例(提取PDF文本) ```c #include <mupdf/fitz.h> // MuPDF主头文件 void extract_text(const char* filename) { fz_context* ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED); fz_register_document_handlers(ctx); // 注册PDF处理器[^2] fz_document* doc = fz_open_document(ctx, filename); int page_count = fz_count_pages(ctx, doc); for (int i = 0; i < page_count; i++) { fz_page* page = fz_load_page(ctx, doc, i); fz_stext_page* text_page = fz_new_stext_page_from_page(ctx, page, NULL); // 提取文本内容 fz_stext_block* block; for (block = text_page->first_block; block; block = block->next) { if (block->type == FZ_STEXT_BLOCK_TEXT) { fz_stext_line* line; for (line = block->u.text.first_line; line; line = line->next) { fz_stext_char* ch; for (ch = line->first_char; ch; ch = ch->next) { putchar(ch->c); // 逐字符输出文本 } putchar('\n'); } } } fz_drop_stext_page(ctx, text_page); fz_drop_page(ctx, page); } fz_drop_document(ctx, doc); fz_drop_context(ctx); } int main() { extract_text("sample.pdf"); // 替换为你的PDF文件 return 0; } ``` --- #### 关键说明 1. **MuPDF 工作流程** - `fz_new_context()`:创建上下文 - `fz_open_document()`:打开PDF文件 - `fz_new_stext_page_from_page()`:提取页面文本对象 - 遍历 `fz_stext_block` 获取文本内容 2. **编译运行** - 按 `Ctrl+Shift+B` 编译(需配置tasks.json) - 终端执行 `./pdf_reader` 运行程序 3. **处理特殊内容** - 图像:使用 `fz_new_pixmap_from_page()` 提取 - 加密PDF:通过 `fz_authenticate_password()` 解密 --- #### 常见问题解决 1. **编译错误:头文件缺失** ```bash sudo apt-get install pkg-config # 确保pkg-config已安装 pkg-config --cflags mupdf # 检查包含路径 ``` 2. **运行时崩溃** 检查PDF文件路径权限,确保使用绝对路径 3. **中文乱码** 安装中文字体包: ```bash sudo apt-get install fonts-wqy-microhei ``` ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值