(原創) 如何使用Standard Library作影像處理? (C/C++) (Image Processing)
在昨天的Blog,我們使用了Standard Library讀寫bmp圖檔,其中的unsigned char *,雖然是一個一維陣列,但骨子是一個二維陣列,該如何實際的做影像處理呢?
先示範一個最簡單的影像處理,產生一個紅色的圖形。
1
/*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : BmpPixelByPixel.cpp
5
Compiler : Visual C++ 8.0 / ANSI C / ISO C++
6
Description : Demo the how to process image pixel by pixel
7
Release : 02/19/2007 1.0
8
*/
9
10
#include "stdio.h"
11
#include "stdlib.h"
12
13
int bmp_read(unsigned char *image, int, int, char *);
14
int bmp_write(unsigned char *image, int, int, char *);
15
16
int main() {
17
unsigned char *image;
18
int xsize = 512;
19
int ysize = 512;
20
21
image = (unsigned char *)malloc((size_t)xsize * ysize * 3);
22
if (image == NULL)
23
return -1;
24
25
for(int y = 0; y != ysize; ++y) {
26
for(int x = 0; x != xsize; ++x) {
27
// set (R,G,B) = (255,0,0)
28
// R
29
*(image + 3 * (y * xsize + x) + 2) = 255;
30
// G
31
*(image + 3 * (y * xsize + x) + 1) = 0;
32
// B
33
*(image + 3 * (y * xsize + x) + 0) = 0;
34
}
35
}
36
37
bmp_write(image, xsize, ysize, "onlyRed");
38
39
free(image);
40
}
41
42
int bmp_read(unsigned char *image, int xsize, int ysize, char *filename) {
43
char fname_bmp[128];
44
sprintf(fname_bmp, "%s.bmp", filename);
45
46
FILE *fp;
47
if (!(fp = fopen(fname_bmp, "rb")))
48
return -1;
49
50
unsigned char header[54];
51
fread(header, sizeof(unsigned char), 54, fp);
52
fread(image, sizeof(unsigned char), (size_t)(long)xsize * ysize * 3, fp);
53
54
fclose(fp);
55
return 0;
56
}
57
58
int bmp_write(unsigned char *image, int xsize, int ysize, char *filename) {
59
unsigned char header[54] = {
60
0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0,
61
54, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0,
62
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63
0, 0, 0, 0
64
};
65
66
long file_size = (long)xsize * (long)ysize * 3 + 54;
67
header[2] = (unsigned char)(file_size &0x000000ff);
68
header[3] = (file_size >> 8) & 0x000000ff;
69
header[4] = (file_size >> 16) & 0x000000ff;
70
header[5] = (file_size >> 24) & 0x000000ff;
71
72
long width = xsize;
73
header[18] = width & 0x000000ff;
74
header[19] = (width >> 8) &0x000000ff;
75
header[20] = (width >> 16) &0x000000ff;
76
header[21] = (width >> 24) &0x000000ff;
77
78
long height = ysize;
79
header[22] = height &0x000000ff;
80
header[23] = (height >> 8) &0x000000ff;
81
header[24] = (height >> 16) &0x000000ff;
82
header[25] = (height >> 24) &0x000000ff;
83
84
char fname_bmp[128];
85
sprintf(fname_bmp, "%s.bmp", filename);
86
87
FILE *fp;
88
if (!(fp = fopen(fname_bmp, "wb")))
89
return -1;
90
91
fwrite(header, sizeof(unsigned char), 54, fp);
92
fwrite(image, sizeof(unsigned char), (size_t)(long)xsize * ysize * 3, fp);
93
94
fclose(fp);
95
return 0;
96
}

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

本範例試著用此一維陣列作一個最簡單的影像處理,將圖片由右向左作mirror。
1
/*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : BmpRightSideLeft.cpp
5
Compiler : Visual C++ 8.0 / ANSI C / ISO C++
6
Description : Demo the how to right side to left by standard library
7
Release : 02/04/2007 1.0
8
*/
9
10
#include "stdio.h"
11
#include "stdlib.h"
12
13
int bmp_read(unsigned char *, int , int , char *);
14
int bmp_write(unsigned char *, int , int , char *);
15
int bmp_rightsideleft(unsigned char *, unsigned char *, int , int);
16
17
int main() {
18
unsigned char *ori, *tar;
19
int xsize = 512;
20
int ysize = 512;
21
22
ori = (unsigned char *)malloc((size_t)xsize * ysize * 3);
23
tar = (unsigned char *)malloc((size_t)xsize * ysize * 3);
24
25
bmp_read(ori, xsize, ysize, "clena");
26
bmp_rightsideleft(ori, tar, xsize, ysize);
27
bmp_write(tar, xsize, ysize, "clena_rightsideleft");
28
}
29
30
31
int bmp_read(unsigned char *image, int xsize, int ysize, char *filename) {
32
char fname_bmp[128];
33
sprintf(fname_bmp, "%s.bmp", filename);
34
35
FILE *fp;
36
if (!(fp = fopen(fname_bmp, "rb")))
37
return -1;
38
39
unsigned char header[54];
40
fread(header, sizeof(unsigned char), 54, fp);
41
fread(image, sizeof(unsigned char), (size_t)(long)xsize * ysize * 3, fp);
42
43
fclose(fp);
44
return 0;
45
}
46
47
int bmp_write(unsigned char *image, int xsize, int ysize, char *filename) {
48
unsigned char header[54] = {
49
0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0,
50
54, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0,
51
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52
0, 0, 0, 0
53
};
54
55
long file_size = (long)xsize * (long)ysize * 3 + 54;
56
header[2] = (unsigned char)(file_size &0x000000ff);
57
header[3] = (file_size >> 8) & 0x000000ff;
58
header[4] = (file_size >> 16) & 0x000000ff;
59
header[5] = (file_size >> 24) & 0x000000ff;
60
61
long width = xsize;
62
header[18] = width & 0x000000ff;
63
header[19] = (width >> 8) &0x000000ff;
64
header[20] = (width >> 16) &0x000000ff;
65
header[21] = (width >> 24) &0x000000ff;
66
67
long height = ysize;
68
header[22] = height &0x000000ff;
69
header[23] = (height >> 8) &0x000000ff;
70
header[24] = (height >> 16) &0x000000ff;
71
header[25] = (height >> 24) &0x000000ff;
72
73
char fname_bmp[128];
74
sprintf(fname_bmp, "%s.bmp", filename);
75
76
FILE *fp;
77
if (!(fp = fopen(fname_bmp, "wb")))
78
return -1;
79
80
fwrite(header, sizeof(unsigned char), 54, fp);
81
fwrite(image, sizeof(unsigned char), (size_t)(long)xsize * ysize * 3, fp);
82
83
fclose(fp);
84
return 0;
85
}
86
87
int bmp_rightsideleft(unsigned char *ori, unsigned char *tar, int xsize, int ysize) {
88
// x-------
89
// y
90
// |
91
// |
92
// |
93
94
int avgX = (0 + xsize) / 2;
95
for(int y = 0; y != ysize; ++y) {
96
for(int x = 0; x != xsize; ++x) {
97
int tarX = 2 * avgX - x;
98
// R
99
*(tar + 3 * (y * xsize + tarX) + 2) = *(ori + 3 * (y * xsize + x) + 2);
100
// G
101
*(tar + 3 * (y * xsize + tarX) + 1) = *(ori + 3 * (y * xsize + x) + 1);
102
// B
103
*(tar + 3 * (y * xsize + tarX) + 0) = *(ori + 3 * (y * xsize + x) + 0);
104
}
105
}
106
107
return 0;
108
}

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

125行到135行為實際的一個pixel一個pixel作影像處理。
原圖

執行結果

Remark
在撰寫處理陣列的迴圈時,應該先從z,再y,最後才是x,為什麼呢?因為當宣告陣列時,是int ia[sizey][sizex],所以是先y,然後才x。
See Also
(原創) 如何使用ANSI C/ISO C++讀寫bmp圖檔? (C/C++)