打开MPI以在PGM文件中分发和操作2d数组

时间:2011-02-22 07:41:08

标签: c mpi multidimensional-array pgm

我需要使用Open MPI在10台正在运行的计算机中分配PGM文件中的2d阵列。然后我需要操纵数组的每个值以获得负图像(255-i)然后打印输出。我正在考虑使用mpi_scattermpi_gather来分发数据。现在的问题是如何将2-d数组读入子数组并将子数组发送到每个工作计算机以进行操作。我正在用C编写这个程序。

任何人都可以帮我解决这个问题或提出想法吗?谢谢。

以下是PGM文件中的数组示例:

P2
# created by 'xv balloons_bw.tif'
640 480
255
232 227 220 216 212 209 207 206 205 205 205 207 208 209 210 211 212 
211 211 213 212 211 210 209 210 210 211 212 211 210 210 210 210 211 
210 210 210 210 209 210 209 208 209 208 209 210 209 208 210 209 209 
208 208 208 209 208 208 208 207 207 207 206 207 207 207 207 207 207 
207 207 207 207 205 204 206 205 205 204 204 204 203 202 203 202 201 
201 201 200 199 199 200 199 198 198 198 197 197 198 197 196 195 195 
194 193 192 192 191 191 190 190 190 190 189 189 190 188 188 188 187 
187 187 186 186 186 186 187 186 186 187 188 188 187 186 186 186 185 
186 186 186 187 186 186 186 185 185 187 186 185 186 185 185 186 185 
184 185 186 185 186 186 186 185 186 185 185 185 184 183 184 184 183 

2 个答案:

答案 0 :(得分:1)

读取PGM文件的最简单方法是使用libpgm包中的netpbm

使用以下方法读取pgm文件:

gray **image;
FILE *fp;
int cols; # num columns
int rows; # num rows
int maxval; # max grayscale value

fp = fopen("input.pgm","r");
image = pgm_readpgm( fp, &cols, &rows, &maxval); 

现在,您可以通过循环遍历行/列来获取负图像:

for (i = 0; i < rows; i++)
    for (j = 0; j < cols; j++)
        image[i][j] = maxval - image[i][j];

棘手的一点是在MPI节点上分配任务,因为image在内存中可能不连续(我没有检查过)。可以深入研究the code以确定存储模式并相应地分散/收集数组,但是不能保证它在将来不会发生变化(不太可能,但可能)并且会破坏您的代码。

可能但非最佳方法是创建一个临时缓冲区,该缓冲区在内存中是连续的,然后分发该缓冲区并在以后重建图像。 E.g。

gray *buffer = malloc(sizeof(gray) * rows * cols);
for (i = 0; i < rows; i++)
    for (j = 0; j < cols; j++)
        buffer[(i*cols)+j] = image[i][j];

现在,我们已经准备好了

  1. 跨节点的分散缓冲区
  2. 您可能需要向每个节点广播maxval
  3. 每个节点执行buffer[n] = maxval - buffer[n];
  4. 将缓冲区收回主人
  5. 重建输出图像
  6. 您可以通过将图片写回image数据来重建图片,或者如果您熟悉the format

    ,只需手动打印出pgm文件

    对于用于MPI操作的数据类型,MPI_UNSIGNED将起作用,因为graya typedef of unsigned int。但是,要严格向前兼容,您可以使用MPI_BYTE并将send_count乘以sizeof(gray)

    不使用libpgm

    如果您想手动读取文件,那么由于您的PGM文件采用纯文本格式(P2而不是P5),因此实际上并不太难。

    假设格式有效,您需要:

    1. 打开文件
    2. 略过前2行
    3. 读入列和行:fscanf(fp,"%d %d", &cols, &rows);
    4. 读入maxval:fscanf(fp,"%d", &maxval);
    5. 根据colsrows
    6. 分配缓冲区
    7. 通过循环col / rows并重复fscanf(fp,"%d", &buffer[r][c]);
    8. 来读取图像的其余部分

答案 1 :(得分:0)

我通常会同意Shawn Chin关于使用现有库进行文件读取的问题;在这种情况下,我可能不同意,因为文件格式非常简单,MPI知道如何在内存中布局数据非常重要。分配为nxm的连续1-d数组的2d nxm数组与遍布内存的行非常不同!与往常一样,这是C没有真正的多维阵列的错误。另一方面,您可以查看libnetpbm库并查看它是如何分配的,或者正如Shawn建议的那样,在读取之后将整个内容复制到连续的内存中。

另请注意,使用(二进制)P5格式实际上会更容易,因为可以使用MPI-IO在开始时并行读取数据,而不是让一个处理器执行所有读取并使用分散/收集进行数据分发。使用ascii文件,您永远不会知道记录的长度,这使得协调的I / O非常困难。

另请注意,这确实不是一个2D问题 - 您只是对阵列的每个部分进行元素操作。因此,只需将数据视为一维数组并忽略几何,就可以大大简化操作。如果您(例如)对图像应用2d滤镜,则不会,因为几何体很重要,您必须相应地对数据进行分区;但在这里我们不在乎。

最后,即使在这种简单的情况下,您也必须使用scatterv和gatherv,因为图像中的单元格数可能不会平均除以MPI任务的数量。你可以通过填充数组来简化逻辑,使其均匀分割;那么你可以 避免一些额外的步骤。

因此,如果您知道将read_pgm()write_pgm()返回指向单个连续内存块的指针,则可以执行以下操作:

int main(int argc, char **argv) {
    int ierr;
    int rank, size;
    int **greys;
    int rows, cols, maxval;
    int ncells;
    int mystart, myend, myncells;
    const int IONODE=0;
    int *disps, *counts, *mydata;
    int *data;

    ierr = MPI_Init(&argc, &argv);
    if (argc != 3) {
        fprintf(stderr,"Usage: %s infile outfile\n",argv[0]);
        fprintf(stderr,"       outputs the negative of the input file.\n");
        return -1;
    }            

    ierr  = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    ierr |= MPI_Comm_size(MPI_COMM_WORLD, &size);
    if (ierr) {
        fprintf(stderr,"Catastrophic MPI problem; exiting\n");
        MPI_Abort(MPI_COMM_WORLD,1);
    }

    if (rank == IONODE) {
        if (read_pgm(argv[1], &greys, &rows, &cols, &maxval)) {
            fprintf(stderr,"Could not read file; exiting\n");
            MPI_Abort(MPI_COMM_WORLD,2);
        }
        ncells = rows*cols;
        disps = (int *)malloc(size * sizeof(int));
        counts= (int *)malloc(size * sizeof(int));
        data = &(greys[0][0]); /* we know all the data is contiguous */
    }

    /* everyone calculate their number of cells */
    ierr = MPI_Bcast(&ncells, 1, MPI_INT, IONODE, MPI_COMM_WORLD);
    myncells = ncells/size;
    mystart = rank*myncells;
    myend   = mystart + myncells - 1;
    if (rank == size-1) myend = ncells-1;
    myncells = (myend-mystart)+1;
    mydata = (int *)malloc(myncells * sizeof(int));

    /* assemble the list of counts.  Might not be equal if don't divide evenly. */
    ierr = MPI_Gather(&myncells, 1, MPI_INT, counts, 1, MPI_INT, IONODE, MPI_COMM_WORLD);
    if (rank == IONODE) {
        disps[0] = 0;
        for (int i=1; i<size; i++) {
            disps[i] = disps[i-1] + counts[i-1];
        }
    }

    /* scatter the data */
    ierr = MPI_Scatterv(data, counts, disps, MPI_INT, mydata, myncells, 
                        MPI_INT, IONODE, MPI_COMM_WORLD);

    /* everyone has to know maxval */
    ierr = MPI_Bcast(&maxval, 1, MPI_INT, IONODE, MPI_COMM_WORLD);

    for (int i=0; i<myncells; i++)
        mydata[i] = maxval-mydata[i];

    /* Gather the data */
    ierr = MPI_Gatherv(mydata, myncells, MPI_INT, data, counts, disps, 
                        MPI_INT, IONODE, MPI_COMM_WORLD);

    if (rank == IONODE) {
        write_pgm(argv[2], greys, rows, cols, maxval);
    }

    free(mydata);
    if (rank == IONODE) {
        free(counts);
        free(disps);
        free(&(greys[0][0]));
        free(greys);
    }
    MPI_Finalize();
    return 0;
}