0%

32位BMP格式转YUV格式&并行计算

前言

首先介绍32位BMP格式的图片转YUV420P格式。然后介绍如何使用openmpi进行并行。
下载地址—>戳我


使用file命令查看文件格式

首先还是来看看如何查看文件的格式。原本想做jpg和png这种类型的转换,但是发现这种类型较为复杂,最后还是选择的BMP和YUV的格式。
使用方法:file 文件名
file命令


BMP文件格式介绍

BMP格式里面可以看看–>BMP百度经验其中的对应数据结构部分

BMP文件的组成:

  • BMP文件头(14Byte):提供文件的格式、大小等信息
  • 位图信息头(40Byte):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
  • 颜色信息:对应图像数据的顺序,32位中为BGRA
  • 图形数据(数据大小):图像数据,上下镜像储存

我是用的是32位的BMP格式(BGRA),没有用24位BMP格式(BGR)。图象数据BGRA:默认的BMP是不支持ALPHA通道的,但对32位BMP而言,每个象素用32位(4个字节)表示,前三个字节表示RGB分量,最后一个字节可以做为ALPHA通道的值,因此32位位图可以存储带ALPHA通道的图像,在文件中,各分量的存储顺序为BGRA,BGRA,BGRA,BGRA…
要注意的是,BMP图像的象素存储顺序是从下到上,如果按照正常的顺序,是上下镜像,需要自己翻转,如果不反转,情况如下图。
bmp储存顺序


YUV文件格式介绍

想要转换成YUV的格式,那么当然需要知道YUV的储存方式,YUV下面的格式有很多种,这里我使用的是YUV420P格式,可以看一下这个介绍–>- 图文详解YUV420数据格式

对于YUV文件,“Y”表示明亮度(Luminance或 Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。在文件中的储存直接保存的是数据文件。由RGB通过公式转化成YUV格式,每个点的RGB值和YUV值一一对应,对于储存的方式有点特别,有一定的压缩。
储存顺序是Y(width*height)-U(width*height/4)-V(width*height/4),转化出来之后的大小是原来数据的1/2,其在内存中占用的空间是width*height*1.5(BYTE),注意是每四个点使用一个UV值,注意下图的颜色对应。其数据储存的顺序如下:
YUV420P储存
其转换标准公式如下,本次使用到了RGB转YUV的公式:

1
2
3
4
5
6
7
8
9
RGB to YUV Conversion
Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16
Cr = V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
Cb = U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128

YUV to RGB Conversion
B = 1.164(Y - 16) + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
R = 1.164(Y - 16) + 1.596(V - 128)

使用mplayer查看YUV文件

因为YUV格式的数据不方便打开,在Linux上不好查看,这里使用一个叫做mplayer的工具来进行图片的查看

1
2
3
4
5
$ sudo apt-get install mplayer
//如果显示有依赖不全的情况,执行这一句
$ sudo apt-get install -f
//图片查看的命令,注意替换文件名和长宽还有格式
$ mplayer -demuxer rawvideo -rawvideo w=1920:h=1080:format=i420 test.yuv -loop 0

mplayer用法介绍

这里简单介绍一下mplayer的使用

1
2
3
4
5
6
//input0对应/dev/video0,查看摄像头内容
mplayer tv:// -tv driver=v4l2:input=0:width=640:height=480:fps=25 -vo x11
//在mplayer中查看YUV格式的图片或视频,可使用如下命令:
$ mplayer -demuxer rawvideo -rawvideo w=1920:h=1080:format=i420 test.yuv -loop 0
//查看mplayer支持的格式
$ mplayer -rawvideo format=help

注意:因为本次课设使用的YUV的420格式.-loop 0为循环播放
format的可选项:420(yv12,i420),422(yuy2,uyvy)
更多格式可以使用上面的命令查看。


得到32位BMP图片

首先还是需要拿到素材,我们使用的是32位BMP图片。Ubuntu自带的截图得到的png是不带A通道的png图片。我这里使用的是crosscover安装的QQ8.5,使用QQ的截图,得到png图片,然后使用opencv进行转换,会自动转换成32位BMP图片,得到方法不重要,使用file xx.bmp,是32位BMP即可。我是用的opencv的方式进行转化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "cv.h"
#include "highgui.h"
#include <cstdio>
#include <iostream>
using namespace std;

int main(int argc,char ** argv)
{
char filename[100] = "test.png";
IplImage *img = cvLoadImage(filename,-1);
cvShowImage("example", img);
cvWaitKey(0);
cvSaveImage("test.bmp",img);
cvReleaseImage(&img);
}

然后命令行编译运行

1
2
3
4
5
//编译转换程序
$ g++ png2bmp.cpp -o png2bmp `pkg-config --cflags --libs opencv`
//运行转换程序
$ ./png2bmp test.png test.bmp
//这样就可以得到我们需要用到的bmp格式的文件了

BMP转换YUV

首先还是先写普通的转换程序,写好之后再改成openmpi的并行。
bmp2yuyv.cpp文件内容如下:

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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;

#define TMAPFILEHEADERLENGTH 14 // The bmp FileHeader length is 14
#define BM 19778 // The ASCII code for BM

static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];


void InitLookupTable();

int RGBtoYUV (int x_dim,int y_dim,void *bmp, void *y_out, void *u_out, void *v_out)
{
long i, j,size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pv1, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
InitLookupTable();
size=x_dim*y_dim;
y_buffer = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buffer && v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}


b = (unsigned char *)bmp;
y = y_buffer;
u = u_buffer;
v = v_buffer;
pu1 =u_buffer;
pv1= v_buffer;
psu = sub_u_buf;
psv = sub_v_buf;

for (j = 0; j < y_dim; j++){
for (i = 0; i < x_dim; i++)
{
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 4;
y ++;
u ++;
v ++;
}
}


for (j=0; j< y_dim/2; j++)
{
for (i=0; i< x_dim/2; i++)
{
*psu=*pu1;
*psv=*pv1;
psu++;
psv++;
pv1+=2;
pu1+=2;
}
pu1+=x_dim;
pv1+=x_dim;
}
free(u_buffer);
free(v_buffer);
return 0;
}


void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}


void bmp_file_test(FILE* fpbmp)
{
unsigned short bfType = 0;
fseek(fpbmp, 0L, SEEK_SET);
fread(&bfType, sizeof(char), 2, fpbmp);
if (BM != bfType)
{
printf("This file is not bmp file.!!!\n");
exit(1);
}
}

//翻转数组,因为bmp图片是反着存的
void swapU(unsigned char *a,unsigned char *b)
{
unsigned char temp;
for(int i=0;i<4;i++){
temp=*a;
*a=*b;
*b=temp;
a++,b++;
}
}
void reversePic(void *bmp,int width,int height)
{
unsigned char *b=(unsigned char *)bmp;
for(int i=0;i<height/2;i++){
int posUp=i*width;
int posDown=(height-i-1)*width;
for(int j=0;j<width;j++){
int up=posUp+j;
int down=posDown+j;
//swap
swapU(b+up*4,b+down*4);
}
}
}

int main(int argc,char **argv)
{
if(argc != 3){
perror("./a.out rgb_name yuv_name");
exit(0);
}
unsigned int width, height, pixelBit,temp;

FILE* bmpfile =NULL;
FILE* rgbtmp =NULL;
FILE* yuvfile =NULL;
unsigned char* rgbbuf =NULL;
unsigned char* ybuf =NULL;
unsigned char* ubuf =NULL;
unsigned char* vbuf =NULL;


if((bmpfile = fopen(argv[1], "rb+"))==NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
if((rgbtmp = fopen("temp.rgb", "wb+"))==NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
if((yuvfile = fopen(argv[2],"wb")) == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}

//判断是否是bmp图片
bmp_file_test(bmpfile);
//获得图片的长宽还有像素占用的位数
fseek(bmpfile, 18L, SEEK_SET);
fread(&temp, sizeof(unsigned int), 1, bmpfile);
width = temp;
fseek(bmpfile, 22L, SEEK_SET);
fread(&temp, sizeof(unsigned int), 1, bmpfile);
height = temp;
fseek(bmpfile, 28L, SEEK_SET);
fread(&temp, 2*sizeof(char), 1, bmpfile);
pixelBit = temp; //这里的大小是一个字(2个字节)
printf("width = %d , height = %d , pixelBit = %d\n", width, height,pixelBit);

rgbbuf = (unsigned char*)malloc(width *height*4 );
ybuf = (unsigned char*)malloc(width * height);
ubuf = (unsigned char*)malloc((width * height) / 4);
vbuf = (unsigned char*)malloc((width * height) / 4);
if (rgbbuf == NULL || ybuf == NULL || ubuf == NULL || vbuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
fseek(bmpfile, 54L, SEEK_SET); //定位到像素点信息位置
fread (rgbbuf, 1, width * height * 4, bmpfile);
//bmp是倒着存的,翻转图像
reversePic(rgbbuf,width,height);
fwrite(rgbbuf, 1, width * height * 4, rgbtmp);
fclose(bmpfile);
fread(rgbbuf, 1, width * height * 4, rgbtmp );
if(RGBtoYUV(width, height, rgbbuf, ybuf, ubuf, vbuf)==0){
printf("图片转换成功\n");
}

fwrite(ybuf, 1, width*height, yuvfile);
fwrite(ubuf, 1, (width*height) / 4, yuvfile);
fwrite(vbuf, 1, (width*height) / 4, yuvfile);

fclose(rgbtmp);
fclose(yuvfile);
free(rgbbuf);
free(ybuf);
free(ubuf);
free(vbuf);
return(0);
}

然后将这个文件进行编译运行就能转换得到YUV文件了

1
2
3
4
5
6
7
8
9
//编译程序
$ cd bmp2yuvyv/
$ g++ bmp2yuyv.cpp -o bmp2yuyv
//进行转换,会有打印信息
$ ./bmp2yuyv test.bmp test.yuv
width = 1920 , height = 1080 , pixelBit = 32
图片转换成功
//显示转换出来的YUV图片
$ mplayer -demuxer rawvideo -rawvideo w=1920:h=1080:format=i420 test.yuv -loop 0

使用openmpi并行

有了普通的转化程序,接下来的任务就是将我们原本的程序并行化,思想就是通过输入来进行动态的调整,将图片的转换按行进行并行。单文件写比较麻烦,这里使用了makefile来进行管理。
文件结构就是makefile,read.cpp,write.cpp,mpi.cpp,将一些并行化的处理等写在了mpi.cpp中(write中啥都没写,本来准备一下分离一下代码,因为懒)直接放代码吧,以上代码会在文末给出下载链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#makefile
all: program

program: mpi.o read.o write.o
mpic++ mpi.o read.o write.o -o program

mpi.o: mpi.cpp
mpic++ -c mpi.cpp -o mpi.o
#mpicc -o mpi.o mpi.c

read.o: read.cpp
g++ -c read.cpp -o read.o

write.o: write.cpp
g++ -c write.cpp -o write.o

clean:
rm -f write.o read.o mpi.o program core *~
1
2
3
4
5
6
7
8
//read.h
#include<cstdio>
#include<cstdlib>

void bmp_file_check(FILE* fpbmp); //判断是否是BMP图片文件
void swapU(unsigned char *a,unsigned char *b);
void reversePic(void *bmp,int width,int height);
void read_pic_more_data(FILE *bmpfile,int *width,int *height,int *pixelBit,int *temp);
1
2
//write.h
void save();
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
//read.cpp
#include"read.h"
#define BM 19778 // The ASCII code for BM

void bmp_file_check(FILE* fpbmp)
{
unsigned short bfType = 0;
fseek(fpbmp, 0L, SEEK_SET);
fread(&bfType, sizeof(char), 2, fpbmp);
if (BM != bfType)
{
printf("This file is not bmp file.!!!\n");
exit(1);
}
}

//翻转数组,因为bmp图片是反着存的
void swapU(unsigned char *a,unsigned char *b)
{
unsigned char temp;
for(int i=0;i<4;i++){
temp=*a;
*a=*b;
*b=temp;
a++,b++;
}
}
void reversePic(void *bmp,int width,int height)
{
unsigned char *b=(unsigned char *)bmp;
for(int i=0;i<height/2;i++){
int posUp=i*width;
int posDown=(height-i-1)*width;
for(int j=0;j<width;j++){
int up=posUp+j;
int down=posDown+j;
//swap
swapU(b+up*4,b+down*4);
}
}
}
//读取宽高大小数据
void read_pic_more_data(FILE *bmpfile,int *width,int *height,int *pixelBit,int *temp)
{
//判断是否是bmp图片
bmp_file_check(bmpfile);
//获得图片的长宽还有像素占用的位数
fseek(bmpfile, 18L, SEEK_SET);
fread(temp, sizeof(unsigned int), 1, bmpfile);
*width = *temp;
fseek(bmpfile, 22L, SEEK_SET);
fread(temp, sizeof(unsigned int), 1, bmpfile);
*height = *temp;
fseek(bmpfile, 28L, SEEK_SET);
fread(temp, 2*sizeof(char), 1, bmpfile);
*pixelBit = *temp; //这里的大小是一个字(2个字节)
// printf("width = %d , height = %d , pixelBit = %d\n", *width, *height, *pixelBit);
}
1
2
3
4
5
//write.cpp
#include"write.h"

void save()
{}
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
//mpi.cpp
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<mpi.h>
#include<cstring>
#include"read.h"
#include"write.h"
using namespace std;
// #define uint unsigned int


static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];


void InitLookupTable();

int RGBtoYUV (int width,int height,void *bmp,void *y_out, void *u_out, void *v_out)
{
long i,j,size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pv1, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
InitLookupTable();
size=width*height;
y_buffer = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buffer && v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}


b = (unsigned char *)bmp;
y = y_buffer;
u = u_buffer;
v = v_buffer;
pu1 =u_buffer;
pv1= v_buffer;
psu = sub_u_buf;
psv = sub_v_buf;

for (j = 0; j < height; j++){
for (i = 0; i < width; i++)
{
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 4;
y ++;
u ++;
v ++;
}
}


for (j=0; j< height/2; j++)
{
for (i=0; i< width/2; i++)
{
*psu=*pu1;
*psv=*pv1;
psu++;
psv++;
pv1+=2;
pu1+=2;
}
pu1+=width;
pv1+=width;
}
free(u_buffer);
free(v_buffer);
return 0;
}


void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}


int main(int argc,char **argv)
{
//mpi-data
double startTime,endTime;
int id;/*进程序号*/
int p; /*进程总数*/
int from,to;/*发送接收序号*/
int tag=0;/*消息标签*/
int tran_height;/*消息储存*/
MPI_Status status;/*return status for*/
/*启动MPI*/
MPI_Init(&argc,&argv);
startTime=MPI_Wtime();
/*查找进程号*/
MPI_Comm_rank(MPI_COMM_WORLD,&id);
/*查找进程总数*/
MPI_Comm_size(MPI_COMM_WORLD,&p);

//my-program-data
char srcfile[]="test.bmp";
char dstfile[]="test.yuv";
int width, height, pixelBit,temp;

FILE* bmpfile =NULL;
FILE* rgbtmp =NULL;
FILE* yuvfile =NULL;
unsigned char* rgbbuf =NULL;
unsigned char* ybuf =NULL;
unsigned char* ubuf =NULL;
unsigned char* vbuf =NULL;
if((bmpfile = fopen(srcfile, "rb+"))==NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
if((rgbtmp = fopen("temp.rgb", "wb+"))==NULL)
{
printf("cannot find rgb file\n");
exit(1);
}
if((yuvfile = fopen(dstfile,"wb")) == NULL)
{
printf("cannot find yuv file\n");
exit(1);
}
read_pic_more_data(bmpfile,&width,&height,&pixelBit,&temp);
if(width%2==1||height%2==1){
printf("输入图片宽或高为奇数,请修改图片\n");
}
MPI_Barrier(MPI_COMM_WORLD);

if(id==0){
//主进程进行运行
rgbbuf = (unsigned char*)malloc(width *height*4 );
ybuf = (unsigned char*)malloc(width * height);
ubuf = (unsigned char*)malloc((width * height) / 4);
vbuf = (unsigned char*)malloc((width * height) / 4);
if (rgbbuf == NULL || ybuf == NULL || ubuf == NULL || vbuf == NULL)
{
printf("no enought memory\n");
exit(1);
}
fseek(bmpfile, 54L, SEEK_SET); //定位到像素点信息位置
fread (rgbbuf, 1, width * height * 4, bmpfile);
//bmp是倒着存的,翻转图像
reversePic(rgbbuf,width,height);
fwrite(rgbbuf, 1, width * height * 4, rgbtmp);
fclose(bmpfile);

int aver_height=height/(p-1);
int rem_height=height%(p-1);
unsigned char *tran_pos=rgbbuf;
int *tran=(int *)malloc(p*sizeof(int));
tran[0]=0;
for(int i=1;i<p;i++){
if(i<=rem_height)
tran[i]=aver_height+1;
else
tran[i]=aver_height;
}
for(int i=1;i<p;i++){
if(tran[i]%2!=0){
//因为总和是4的倍数,所以不用管最后一项可能为奇数
tran[i]--;
tran[i+1]++;
}
}
//分发数据,将需要转换的指针头部位置发给子进程
for(to=1;to<p;to++){
MPI_Send(&tran[to],1,MPI_INT,to,tag,MPI_COMM_WORLD);
MPI_Send(tran_pos,tran[to]*width*4,MPI_UNSIGNED_CHAR,to,tag,MPI_COMM_WORLD);
tran_pos+=tran[to]*width*4;
}
//从子进程接收数据
int posy=0,posu=0,posv=0;
for(to=1;to<p;to++){
MPI_Recv(ybuf+posy,width*tran[to],MPI_UNSIGNED_CHAR,to,tag,MPI_COMM_WORLD,&status);
MPI_Recv(ubuf+posu,width*tran[to]/4,MPI_UNSIGNED_CHAR,to,tag,MPI_COMM_WORLD,&status);
MPI_Recv(vbuf+posv,width*tran[to]/4,MPI_UNSIGNED_CHAR,to,tag,MPI_COMM_WORLD,&status);
posy+=tran[to]*width;
posu+=tran[to]*width/4;
posv+=tran[to]*width/4;
}
fwrite(ybuf, 1, width*height, yuvfile);
fwrite(ubuf, 1, (width*height) / 4, yuvfile);
fwrite(vbuf, 1, (width*height) / 4, yuvfile);
//关闭文件流指针
fclose(rgbtmp);
fclose(yuvfile);
free(tran);
free(rgbbuf);
free(ybuf);
free(ubuf);
free(vbuf);
}else{
//接收主进程分发过来的数据,tran_height表示需要修改的
MPI_Recv(&tran_height,1,MPI_INT,0,tag,MPI_COMM_WORLD,&status);
cout<<id<<":"<<tran_height<<endl;
unsigned char * tran_buf = (unsigned char*)malloc(width *tran_height*4 );
MPI_Recv(tran_buf,width*tran_height*4,MPI_UNSIGNED_CHAR,0,tag,MPI_COMM_WORLD,&status);
//根据传来的tran_buf进行转换
ybuf = (unsigned char*)malloc(width * tran_height);
ubuf = (unsigned char*)malloc((width * tran_height) / 4);
vbuf = (unsigned char*)malloc((width * tran_height) / 4);
if(RGBtoYUV(width, tran_height, tran_buf, ybuf, ubuf, vbuf)==0){
// cout<<id<<":转换成功"<<endl;
}
//将YUV三个通道的内容回传给0号进程
MPI_Send(ybuf,width*tran_height,MPI_UNSIGNED_CHAR,0,tag,MPI_COMM_WORLD);
MPI_Send(ubuf,width*tran_height/ 4,MPI_UNSIGNED_CHAR,0,tag,MPI_COMM_WORLD);
MPI_Send(vbuf,width*tran_height/ 4,MPI_UNSIGNED_CHAR,0,tag,MPI_COMM_WORLD);
}
endTime=MPI_Wtime();
// cout<<(int)startTime/3600<<"h:"<<((int)startTime%3600)/60<<"m:"<<(int)startTime%600<<"s:"<<endl;
MPI_Barrier(MPI_COMM_WORLD);
if(id==0)
cout<<"time:"<<(endTime-startTime)<<endl;
//拦截所有进程
/*关闭MPI*/
MPI_Finalize();
return 0;
}

编译和运行方法

1
2
3
4
5
6
7
8
9
10
11
12
//使用make方法
$ cd hpc-cal/
$ make program
//使用openmpi运行,这里指定4个进程,输出其他进程的处理行数
//不得少于2个,0号进程做分发
$ mpiexec -np 4 program
1:360
2:360
3:360
time:0.0517164
//查看转化结果
$ mplayer -demuxer rawvideo -rawvideo w=1920:h=1080:format=i420 1.yuv -loop 0

坑点

这个程序是做完了,但是有个坑点,就是传入图片的长宽像素值不能为奇数,必须都是偶数。
因为在转化成YUV格式的时候其中的u和v的值是(width*height)/4;,也就是每四个点共用一个UV的值,其实可以在程序中进行处理,但是对于列的处理比较麻烦,这里也就没有继续做优化了。


参考资料

听说好看的人都关注了我的公众号《泫言》