在昨天的Blog,我們使用了Standard Library讀寫bmp圖檔,其中的unsigned char *,雖然是一個一維陣列,但骨子是一個二維陣列,該如何實際的做影像處理呢?

先示範一個最簡單的影像處理,產生一個紅色的圖形。

1/**//* 
2(C)
4Filename : BmpPixelByPixel.cpp
5Compiler : Visual C++ 8.0 / ANSI C / ISO C++
6Description : Demo the how to process image pixel by pixel
7Release : 02/19/2007 1.0
8*/
9
10#include "stdio.h"
11#include "stdlib.h"
12
13int bmp_read(unsigned char *image, int, int, char *);
14int bmp_write(unsigned char *image, int, int, char *);
15
16int 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
42int 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
58int 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}

本範例試著用此一維陣列作一個最簡單的影像處理,將圖片由右向左作mirror。

1/**//*
2(C)
4Filename : BmpRightSideLeft.cpp
5Compiler : Visual C++ 8.0 / ANSI C / ISO C++
6Description : Demo the how to right side to left by standard library
7Release : 02/04/2007 1.0
8*/
9
10#include "stdio.h"
11#include "stdlib.h"
12
13int bmp_read(unsigned char *, int , int , char *);
14int bmp_write(unsigned char *, int , int , char *);
15int bmp_rightsideleft(unsigned char *, unsigned char *, int , int);
16
17int 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
31int 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
47int 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
87int 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}


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

 

Remark

在撰寫處理陣列的迴圈時,應該先從z,再y,最後才是x,為什麼呢?因為當宣告陣列時,是int ia[sizey][sizex],所以是先y,然後才x。