MPI - 将部分图像发送到不同的进程

时间:2015-06-05 18:24:20

标签: c++ opencv mpi

我正在编写一个程序,其中进程0将部分映像发送到其他进程,这些进程转换(长操作)此部分并发送回等级0.我有一件事有问题。为了重现我的问题,我写了一个简单的例子。大小为512x512px的图像按进程0分为4个部分(垂直条纹)。其他进程将此部分保存在磁盘上。问题是每个过程都保存了相同的部分。我发现图像在部件上正确分割,但问题可能在于发送数据。我的代码有什么问题?

执行命令

mpirun -np 5 ./example

主:

int main(int argc, char **argv) {

    int size, rank;
    MPI_Request send_request, rec_request;
    MPI_Status status;
    ostringstream s;

    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    if (rank == 0) {

        Mat mat = imread("/home/user/original.jpg", CV_LOAD_IMAGE_COLOR);
        if (!mat.data) exit(-1);

        int idx = 1;
        for (int c = 0; c < 512; c += 128) {
            Mat slice = mat(Rect(c, 0, 128, 512)).clone();
            MPI_Isend(slice.data, 128 * 512 * 3, MPI_BYTE, idx, 0, MPI_COMM_WORLD, &send_request);
            idx++;
        }
    }
    if (rank != 0) {
        Mat test = Mat(512, 128, CV_8UC3);
        MPI_Irecv(test.data, 128 * 512 * 3, MPI_BYTE, 0, 0, MPI_COMM_WORLD, &rec_request);
        MPI_Wait(&rec_request, &status);

        s << "/home/user/p" << rank << ".jpg";
        imwrite(s.str(), test);
    }

    MPI_Finalize();
    return 0;
}

2 个答案:

答案 0 :(得分:2)

如果您坚持使用非阻止操作,那么同时发出多个操作的正确方法是:

MPI_Request *send_reqs = new MPI_Request[4];

int idx = 1;
for (int c = 0; c < 512; c += 128) {
    Mat slice = mat(Rect(c, 0, 128, 512)).clone();
    MPI_Isend(slice.data, 128 * 512 * 3, MPI_BYTE, idx, 0, MPI_COMM_WORLD, &send_reqs[idx-1]);
    idx++;
}

MPI_Waitall(4, send_reqs, MPI_STATUSES_IGNORE);
delete [] send_reqs;

另一个(和更好的恕我直言)选项是利用MPI_Scatterv来分散原始数据缓冲区。因此,您甚至可以保存图像矩阵的克隆部分。

if (rank == 0) {
    Mat mat = imread("/home/user/original.jpg", CV_LOAD_IMAGE_COLOR);
    if (!mat.data) exit(-1);

    int *send_counts = new int[size];
    int *displacements = new int[size];

    // The following calculations assume row-major storage
    for (int i = 0; i < size; i++) {
        send_counts[i] = displacements[i] = 0;
    }
    int idx = 1;
    for (int c = 0; c < 512; c += 128) {
        displacements[idx] = displacements[idx-1] + send_counts[idx-1];
        send_counts[idx] = 128 * 512 * 3;
        idx++;
    }

    MPI_Scatterv(mat.data, send_counts, displacements, MPI_BYTE,
                 NULL, 0, MPI_BYTE, 0, MPI_COMM_WORLD);

    delete [] send_counts;
    delete [] displacements;
}
if (1 <= rank && rank <= 4) {
    Mat test = Mat(512, 128, CV_8UC3);
    MPI_Scatterv(NULL, NULL, NULL, MPI_BYTE,
                 test.data, 128 * 512 * 3, MPI_BYTE, 0, MPI_COMM_WORLD);

    s << "/home/user/p" << rank << ".jpg";
    imwrite(s.str(), test);
}

注意MPI_Scatterv的参数是如何准备的。由于您只分散到4个MPI进程,因此将send_counts[]的某些元素设置为零可以使程序在超过5个MPI进程中正常运行。此外,原始代码中的根级别不会发送给自己,因此send_counts[0]必须为零。

答案 1 :(得分:1)

问题在于,在矩阵MPI_Send被破坏之前,您不会等待发送操作完成。使用MPI_Isend代替Mat

如果您确实想要使用非阻塞通信,则必须跟踪所有MPI_Request对象和所有ons-page图像,直到发送完成为止。