数字图像处理实验1

本次实验课实现了对于16色图像、256色图像、24位真彩图像的读取显示,以及灰度变换功能,对于不同深度的图像结构有了进一步的了解,部分代码展示如下,便于温习巩固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#include "stdafx.h"

BITMAPINFO* lpBitsInfo = NULL;

BOOL LoadBmpFile(char* BmpFileName)
{
FILE* fp;
if(NULL == (fp = fopen(BmpFileName,"rb")))
return FALSE;

BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;

fread(&bf,14,1,fp);
fread(&bi,40,1,fp);

DWORD NumColors;
if (bi.biClrUsed != 0)
NumColors = bi.biClrUsed;
else
{
switch(bi.biBitCount) //图像用到的颜色的位数
{
case 1: // 表示二值图像
NumColors = 2;
break;
case 4: // 表示16色
NumColors = 16;
break;
case 8: // 256色
NumColors = 256;
break;
case 24: // 24位真彩
NumColors = 0;
break;
}
}
DWORD PalSize = NumColors * 4; //调色板的大小
DWORD ImgSize = (bi.biWidth * bi.biBitCount + 31 )/32 * 4 * bi.biHeight;
DWORD Size = 40 + PalSize + ImgSize; //信息头+调色板+信息
if (NULL == (lpBitsInfo = (BITMAPINFO*)malloc(Size)))
return FALSE;

fseek(fp,14,SEEK_SET); //确保指针指向信息头起始位置
fread((char*)lpBitsInfo,Size,1,fp);

lpBitsInfo->bmiHeader.biClrUsed = NumColors;

return TRUE;
}

void Gray()
{
// 获取原始图像的宽度
int w = lpBitsInfo->bmiHeader.biWidth;
// 获取原始图像的高度
int h = lpBitsInfo->bmiHeader.biHeight;
// 计算原始图像每行的字节数,考虑到字节对齐
int LineBytes = (w * lpBitsInfo->bmiHeader.biBitCount + 31 )/ 32 * 4;
// 定位到原始图像的像素数据起始位置,跳过颜色表
BYTE* lpBits = (BYTE*)&lpBitsInfo->bmiColors[lpBitsInfo->bmiHeader.biClrUsed];

// 计算灰度图像每行需要的字节数,同样进行字节对齐
int LineBytes_gray = (w * 8 + 31)/ 32 * 4;
// 为灰度图像分配内存空间,包括信息头、颜色表和像素数据
BITMAPINFO* lpBitsInfo_gray = (BITMAPINFO*)malloc(40 + 1024 + LineBytes_gray * h);

// 复制原始图像的信息头到灰度图像的信息头
memcpy(lpBitsInfo_gray, lpBitsInfo, 40);
// 设置灰度图像的位深度为 8,表示每个像素用一个字节表示,对应 256 种灰度值
lpBitsInfo_gray->bmiHeader.biBitCount = 8;
// 设置灰度图像的颜色表中颜色的数量为 256
lpBitsInfo_gray->bmiHeader.biClrUsed = 256;

// 初始化灰度图像的颜色表,每个颜色表项的红、绿、蓝值都相同,范围从 0 到 255
int i,j;
for(i = 0; i < 256; i++)
{
lpBitsInfo_gray->bmiColors[i].rgbRed = i;
lpBitsInfo_gray->bmiColors[i].rgbGreen = i;
lpBitsInfo_gray->bmiColors[i].rgbBlue = i;
lpBitsInfo_gray->bmiColors[i].rgbReserved = 0;
}

// 定位到灰度图像的像素数据起始位置,跳过颜色表
BYTE* lpBits_gray = (BYTE*)&lpBitsInfo_gray->bmiColors[256];

// 定义指向当前像素的指针
BYTE *pixel;

// 根据原始图像的位深度进行不同的处理
switch(lpBitsInfo->bmiHeader.biBitCount)
{
case 4: // 16 色图像灰度化
for(i = 0; i < h; i++)
{
for(j = 0; j < w; j++)
{
if(j % 2 == 1) // 如果当前列索引是奇数,表示当前字节的低 4 位
{
// 定位到包含低 4 位像素值的字节位置
pixel = lpBits + LineBytes * (h - 1 - i) + j / 2;
// 获取当前字节的低 4 位
BYTE lowFourBits = *pixel & 0x0F;
// 根据低 4 位像素值在颜色表中找到对应的颜色,并获取红、绿、蓝值
int R = lpBitsInfo->bmiColors[lowFourBits].rgbRed;
int G = lpBitsInfo->bmiColors[lowFourBits].rgbGreen;
int B = lpBitsInfo->bmiColors[lowFourBits].rgbBlue;
// 计算红、绿、蓝值的平均值作为灰度值
int avg = (R + B + G) / 3;
// 定位到灰度图像当前像素位置
pixel = lpBits_gray + LineBytes_gray * (h - 1 - i) + j;
// 将计算得到的灰度值存储到灰度图像中
*pixel = avg;
}
else{
// 如果当前列索引是偶数,表示当前字节的高 4 位
// 定位到包含高 4 位像素值的字节位置
pixel = lpBits + LineBytes * (h - 1 - i) + j / 2;
// 获取当前字节的高 4 位
BYTE highFourBits = *pixel >> 4;
// 根据高 4 位像素值在颜色表中找到对应的颜色,并获取红、绿、蓝值
int R = lpBitsInfo->bmiColors[highFourBits].rgbRed;
int G = lpBitsInfo->bmiColors[highFourBits].rgbGreen;
int B = lpBitsInfo->bmiColors[highFourBits].rgbBlue;
// 计算红、绿、蓝值的平均值作为灰度值
int avg = (R + B + G) / 3;
// 定位到灰度图像当前像素位置
pixel = lpBits_gray + LineBytes_gray * (h - 1 - i) + j;
// 将计算得到的灰度值存储到灰度图像中
*pixel = avg;
}
}
}
break;

case 8: // 256 色图像灰度化
for(i = 0; i < h; i++)
{
for(j = 0; j < w; j++)
{
// 定位到原始图像当前像素位置
pixel = lpBits + LineBytes * (h - 1 - i) + j;
// 根据当前像素值在颜色表中找到对应的颜色,并获取红、绿、蓝值
int R = lpBitsInfo->bmiColors[*pixel].rgbRed;
int G = lpBitsInfo->bmiColors[*pixel].rgbGreen;
int B = lpBitsInfo->bmiColors[*pixel].rgbBlue;
// 计算红、绿、蓝值的平均值作为灰度值
int avg = (R + B + G) / 3;
// 定位到灰度图像当前像素位置
pixel = lpBits_gray + LineBytes_gray * (h - 1 - i) + j;
// 将计算得到的灰度值存储到灰度图像中
*pixel = avg;
}
}
break;

case 24: // 24 位真彩图像灰度化
for(i = 0; i < h; i++)
{
for(j = 0; j < w; j++)
{
// 分别定位到当前像素的蓝、绿、红通道的位置
BYTE *B = lpBits + LineBytes * (h - 1 - i) + j * 3;
BYTE *G = B + 1;
BYTE *R = G + 1;
// 计算红、绿、蓝值的平均值作为灰度值
int avg = (*R + *B + *G)/3;
// 定位到灰度图像当前像素位置
pixel = lpBits_gray + LineBytes_gray * (h - 1 - i) + j;
// 将计算得到的灰度值存储到灰度图像中
*pixel = avg;
}
}
break;
}

// 释放原始图像的内存空间
free(lpBitsInfo);
// 将灰度图像的信息结构体指针赋值给全局变量,替换原始图像的信息结构体指针
lpBitsInfo = lpBitsInfo_gray;
}

画程序的流程图或N-S图

使用在线工具:https://mermaid.nodejs.cn/intro/

graph TD
A[开始] --> B[打开位图文件]
B --> C{文件打开成功?}
C -- 是 --> D[读取位图文件头]
D --> E[读取位图信息头]
E --> F[确定颜色数量]
F --> G[计算调色板大小]
G --> H[计算图像大小]
H --> I[分配内存]
I --> J[读取位图信息]
J --> K[设置位图信息头中的颜色使用量]
K --> L[结束]
C -- 否 --> M[返回FALSE]

graph TD
A[开始] --> B[获取位图宽度和高度]
B --> C[计算每行字节数]
C --> D[定位到像素数据]
D --> E[计算灰度图像每行字节数]
E --> F[分配灰度图像内存]
F --> G[复制位图信息头]
G --> H[设置灰度图像位深度]
H --> I[设置灰度图像颜色表大小]
I --> J[初始化灰度图像颜色表]
J --> K[定位到灰度图像像素数据]
K --> L{根据位图位深度处理每个像素}
L -->|4位图像| M[处理每个像素的高低4位]
M --> N[计算灰度值并存储]
L -->|8位图像| O[直接处理每个像素]
O --> P[计算灰度值并存储]
L -->|24位图像| Q[处理每个像素的RGB值]
Q --> R[计算灰度值并存储]
N --> S[释放原始位图内存]
P --> S
R --> S
S --> T[更新位图信息指针]
T --> U[结束]

graph TD
A[开始] --> B{位图信息为空?}
B -- 是 --> C[返回]
B -- 否 --> D[获取位图宽度和高度]
D --> E[计算每行字节数]
E --> F[定位到像素数据]
F --> G{坐标超出边界?}
G -- 是 --> H[返回]
G -- 否 --> I{根据位图位深度获取像素值}
I -->|1位图像| J{是前景点?}
J -- 是 --> K[前景点]
J -- 否 --> L[背景点]
I -->|4位图像| M{处理每个像素的高低4位}
M --> N[获取RGB值并存储]
I -->|8位图像| O[直接处理每个像素]
O --> P{是否为灰度值?}
P -- 是 --> Q[存储灰度值]
P -- 否 --> R[获取RGB值并存储]
I -->|24位图像| S[处理每个像素的RGB值]
S --> T[获取RGB值并存储]
K --> U[将结果存储到字符串中]
L --> U
N --> U
Q --> U
R --> U
T --> U
U --> V[结束]

数字图像处理实验1
http://jrhu0048.github.io/2024/10/15/shu-zi-tu-xiang-chu-li/shu-zi-tu-xiang-chu-li-shi-yan-1/
作者
JR.HU
发布于
2024年10月15日
更新于
2024年12月28日
许可协议