“输入图片”在编程中通常指读取图片文件,并将其数据加载到内存中,以便进行后续处理(如显示、修改、分析等)。

在C语言中,由于其本身不包含高级的图形或图像处理库,所以直接操作图片文件是比较复杂的,图片文件(如JPEG, PNG, BMP)有特定的格式(头部信息、像素数据、压缩算法等),我们需要借助第三方库来简化这个过程。
下面我将从最简单的方式到更专业的方式,为你介绍几种在C语言中输入图片的方法。
使用最简单的BMP格式(不依赖第三方库)
BMP(Bitmap)是一种未经压缩或简单压缩的位图格式,它的结构相对简单,我们可以手动解析它的文件头和像素数据,是学习C语言操作图像格式的绝佳入门案例。
BMP文件基本结构:
- 文件头:存储文件类型、文件大小等信息。
- 信息头:存储图像的宽度、高度、颜色深度等关键信息。
- 像素数据:实际的图像像素数据,通常按行存储,可能存在每行字节数不是4的倍数而需要填充的情况。
示例代码:读取一个24位(真彩色)的BMP文件,并获取其像素数据
这个例子会:

- 定义BMP文件头和信息头的结构体。
- 以二进制模式打开一个BMP文件。
- 读取并解析文件头和信息头。
- 分配内存来存储像素数据。
- 读取像素数据到内存中。
- 关闭文件并释放内存。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> // 用于固定宽度整数类型,如 uint8_t, uint32_t
// 1. 定义BMP文件头 (14 bytes)
#pragma pack(push, 1) // 确保结构体按1字节对齐,避免填充
typedef struct {
uint16_t bfType; // 文件类型,必须是 'BM'
uint32_t bfSize; // 整个文件的大小
uint16_t bfReserved1; // 保留,必须为0
uint16_t bfReserved2; // 保留,必须为0
uint32_t bfOffBits; // 从文件头到像素数据的偏移量
} BMPFileHeader;
// 2. 定义BMP信息头 (40 bytes)
typedef struct {
uint32_t biSize; // 信息头大小,固定为40
int32_t biWidth; // 图像宽度(像素)
int32_t biHeight; // 图像高度(像素),正数表示从下到上,负数表示从上到下
uint16_t biPlanes; // 颜色平面数,必须为1
uint16_t biBitCount; // 每个像素的位数,如1, 4, 8, 16, 24, 32
uint32_t biCompression; // 压缩方式,0表示不压缩
uint32_t biSizeImage; // 像素数据的大小(字节)
int32_t biXPelsPerMeter; // 水平分辨率
int32_t biYPelsPerMeter; // 垂直分辨率
uint32_t biClrUsed; // 使用的颜色数
uint32_t biClrImportant; // 重要的颜色数
} BMPInfoHeader;
#pragma pack(pop)
// 计算每行的填充字节数
int calculate_padding(int width, int bit_count) {
// 每行字节数 = (width * bit_count) / 8
// 每行字节数必须是4的倍数,如果不是则需要填充
int bytes_per_row = (width * bit_count) / 8;
int padding = (4 - (bytes_per_row % 4)) % 4;
return padding;
}
int main() {
const char* filename = "input.bmp"; // 假设你有一个名为input.bmp的文件在同一目录下
FILE* file = fopen(filename, "rb");
if (!file) {
perror("Error opening file");
return 1;
}
// 读取文件头和信息头
BMPFileHeader file_header;
BMPInfoHeader info_header;
fread(&file_header, sizeof(BMPFileHeader), 1, file);
fread(&info_header, sizeof(BMPInfoHeader), 1, file);
// 验证文件类型和格式
if (file_header.bfType != 0x4D42) { // 'BM' in little-endian
fprintf(stderr, "Not a valid BMP file.\n");
fclose(file);
return 1;
}
if (info_header.biBitCount != 24) {
fprintf(stderr, "This example only supports 24-bit BMP files.\n");
fclose(file);
return 1;
}
printf("Image Width: %d\n", info_header.biWidth);
printf("Image Height: %d\n", info_header.biHeight);
printf("Bits per pixel: %d\n", info_header.biBitCount);
// 计算每行的填充字节数
int padding = calculate_padding(info_header.biWidth, info_header.biBitCount);
printf("Padding per row: %d bytes\n", padding);
// 分配内存存储像素数据
// BMP是BGR格式,每个像素3字节
long image_data_size = info_header.biWidth * info_header.biHeight * 3;
unsigned char* pixel_data = (unsigned char*)malloc(image_data_size);
if (!pixel_data) {
perror("Error allocating memory for pixel data");
fclose(file);
return 1;
}
// 将文件指针移动到像素数据的起始位置
fseek(file, file_header.bfOffBits, SEEK_SET);
// 逐行读取像素数据(跳过填充)
for (int i = 0; i < info_header.biHeight; i++) {
// fread一次读取一行的像素数据
size_t bytes_read = fread(pixel_data + (i * info_header.biWidth * 3),
1,
info_header.biWidth * 3,
file);
// 跳过每行末尾的填充字节
fseek(file, padding, SEEK_CUR);
}
printf("Successfully read pixel data into memory.\n");
printf("First pixel (B, G, R): %d, %d, %d\n",
pixel_data[0], pixel_data[1], pixel_data[2]);
// --- 在这里你可以对 pixel_data 进行处理 ---
// 修改像素值、进行图像分析等...
// 清理资源
free(pixel_data);
fclose(file);
return 0;
}
如何编译和运行:
- 准备一张24位色的BMP图片(例如用画图程序另存为
input.bmp)。 - 将上面的代码保存为
read_bmp.c。 - 使用GCC编译:
gcc read_bmp.c -o read_bmp - 运行:
./read_bmp
优点:
- 不需要任何外部库,纯C语言实现。
- 能让你深入了解图像文件的底层结构。
缺点:
- 只能处理BMP格式,对主流的JPEG、PNG等格式无能为力。
- 代码复杂,需要自己处理文件格式细节。
使用成熟的图像处理库(推荐)
在实际开发中,我们几乎总是使用成熟的第三方库来处理图像,因为它们功能强大、稳定且支持多种格式,最常用的是 libjpeg 和 libpng,以及它们的现代封装库。

推荐库:Stb Image (Single-File Libraries)
这是一个非常流行的“单文件库”,你只需要把 stb_image.h 头文件包含到你的项目中即可,它极其轻量、易于使用,并且支持JPEG, PNG, BMP, TGA, PSD, GIF等多种格式。
获取库文件
从 https://github.com/nothings/stb 下载 stb_image.h 文件,并放在你的项目目录中。
示例代码:使用Stb Image读取一张PNG或JPG图片
这个例子会:
- 包含
stb_image.h。 - 使用
stbi_load()函数一键加载图片。 - 获取图片的宽度、高度和通道数。
- 获取像素数据指针。
- 使用后调用
stbi_image_free()释放内存。
#define STB_IMAGE_IMPLEMENTATION // 这个宏必须在包含头文件之前定义一次,它会展开库的实现代码
#include "stb_image.h" // 包含单文件库的头文件
#include <stdio.h>
int main() {
const char* filename = "input.png"; // 可以是 .jpg, .png, .bmp 等格式
// 3. 加载图像
// stbi_load 返回一个指向像素数据的 unsigned char 指针
// 如果加载失败,返回 NULL
int width, height, channels;
unsigned char *pixel_data = stbi_load(filename, &width, &height, &channels, 0);
if (!pixel_data) {
fprintf(stderr, "Error: Could not load image %s\n", filename);
return 1;
}
printf("Successfully loaded image: %s\n", filename);
printf("Image Width: %d\n", width);
printf("Image Height: %d\n", height);
printf("Number of channels: %d\n", channels);
printf("Image format: %s\n",
channels == 1 ? "Grayscale" :
channels == 3 ? "RGB" :
channels == 4 ? "RGBA" : "Unknown");
// pixel_data 是一个一维数组,按行存储像素
// 对于RGB图像,pixel_data[0]是第一个像素的R值,pixel_data[1]是G,pixel_data[2]是B
// pixel_data[3]是第二个像素的R值,以此类推
printf("First pixel (R, G, B): %d, %d, %d\n",
pixel_data[0], pixel_data[1], pixel_data[2]);
// --- 在这里你可以对 pixel_data 进行处理 ---
// 4. 释放图像数据
stbi_image_free(pixel_data);
return 0;
}
如何编译和运行:
- 准备一张
input.png或input.jpg图片。 - 将上面的代码保存为
read_image.c,并将stb_image.h放在同一目录下。 - 使用GCC编译:
gcc read_image.c -o read_image - 运行:
./read_image
优点:
- 极其简单:几行代码就能完成图片加载。
- 支持多种格式:自动处理JPEG、PNG等复杂格式。
- 无需链接:因为是单文件实现,编译时不需要额外链接库文件。
- 跨平台:纯C代码,任何标准C编译器都能用。
缺点:
- 功能相对基础,主要用于读取,对于高级的图像处理(如滤镜、变换),你可能需要另一个库,Stb Image Resize 或 OpenCV。
使用功能强大的计算机视觉库(OpenCV)
如果你的项目需要复杂的图像处理、计算机视觉功能(如人脸识别、物体检测、图像变换等),OpenCV 是不二之选。
安装OpenCV
安装方法因操作系统而异,在Linux上(如Ubuntu)可以使用包管理器:
sudo apt-get install libopencv-dev
在Windows和macOS上,通常需要从官网下载源码或预编译包进行安装。
示例代码:使用OpenCV读取图片
OpenCV的C++接口更常用且功能更完整,但它也提供了C语言接口(#include <opencv2/opencv.h>),这里我们展示更现代的C++方式,因为它更简洁。
#include <opencv2/opencv.hpp> // OpenCV C++头文件
#include <iostream>
int main() {
// cv::imread 是OpenCV中用于读取图像的函数
// cv::IMREAD_COLOR 表示以彩色模式读取(忽略alpha通道)
cv::Mat image = cv::imread("input.jpg", cv::IMREAD_COLOR);
// 检查图像是否成功加载
if (image.empty()) {
std::cerr << "Error: Could not read the image." << std::endl;
return 1;
}
// 打印图像信息
std::cout << "Image Width: " << image.cols << std::endl;
std::cout << "Image Height: " << image.rows << std::endl;
std::cout << "Number of channels: " << image.channels() << std::endl;
// OpenCV中的 cv::Mat 是一个多维数组类,可以直接操作像素
// image.data 指向像素数据的指针
std::cout << "First pixel (B, G, R in OpenCV): "
<< (int)image.data[0] << ", "
<< (int)image.data[1] << ", "
<< (int)image.data[2] << std::endl;
// 显示图像
cv::imshow("Loaded Image", image);
cv::waitKey(0); // 等待用户按下任意键
// OpenCV的cv::Mat会在其生命周期结束后自动释放内存,
// 所以我们不需要手动释放image.data。
return 0;
}
如何编译和运行(以Linux为例):
你需要链接OpenCV的库。
g++ your_program.cpp -o your_programpkg-config --cflags --libs opencv4``
优点:
- 功能极其强大:包含了数千种图像处理和计算机视觉算法。
- 性能优异:底层用C/C++和优化过的汇编编写。
- 生态系统完善:拥有庞大的社区和海量的教程。
缺点:
- 安装和配置相对复杂。
- 库本身非常庞大,不适合只需要简单读取图片的轻量级项目。
总结与选择建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 手动解析BMP | 无需外部库,理解底层原理 | 仅支持BMP,代码复杂,易出错 | 学习C语言文件操作和图像格式,或教学演示 |
| Stb Image | 极其简单,支持多格式,单文件,无依赖 | 功能基础,主要用于读取 | 强烈推荐,绝大多数需要读取图片的C/C++项目。 |
| OpenCV | 功能强大,性能卓越,生态系统完善 | 安装配置复杂,库体积大 | 复杂的图像处理、计算机视觉、科学研究、专业项目 |
给你的建议:
- 如果你是初学者,或者只是想简单地在程序里用一张图片:直接使用 Stb Image,它是最简单、最高效的解决方案。
- 如果你想深入理解图像文件格式:尝试手动解析 BMP 格式,这是一个非常有价值的练习。
- 如果你的项目涉及复杂的图像处理和分析:学习并使用 OpenCV,它是行业标准,虽然入门稍难,但回报巨大。
