一起使用 MPI 和 OpenCV 时出现分段错误
Segmentation Fault while using MPI and OpenCV together
我正在尝试用 C++ 学习 MPI。我对 OpenCV 有一些了解,所以我尝试使用 MPI 和 OpenCV 编写程序。这听起来可能很愚蠢,但出于学习的目的,我尝试从线程 0 上的网络摄像头捕获图像并将图像传递给线程 1 以转换为灰度并显示灰度图像。
这是我编译代码的方式:
mpic++ opencv.cpp `pkg-config opencv --libs`
代码编译成功,但是当我 运行 可执行文件时,屏幕上显示了一个图像几分之一秒,这就是我在终端上看到的
~/mpi$ mpirun -np 2 ./a.out
libv4l2: error setting pixformat: Device or resource busy
HIGHGUI ERROR: libv4l unable to ioctl S_FMT
libv4l2: error setting pixformat: Device or resource busy
libv4l1: error setting pixformat: Device or resource busy
HIGHGUI ERROR: libv4l unable to ioctl VIDIOCSPICT
[arch:09670] *** Process received signal ***
[arch:09670] Signal: Segmentation fault (11)
[arch:09670] Signal code: Address not mapped (1)
[arch:09670] Failing at address: 0x218ac50
[arch:09670] [ 0] /usr/lib/libpthread.so.0(+0x10740)[0x7f422fcac740]
[arch:09670] [ 1] /usr/lib/libopencv_core.so.2.4(_ZNK2cv11_InputArray6getMatEi+0x203)[0x7f4233c8c113]
[arch:09670] [ 2] /usr/lib/libopencv_imgproc.so.2.4(_ZN2cv8cvtColorERKNS_11_InputArrayERKNS_12_OutputArrayEii+0x50)[0x7f4232c25de0]
[arch:09670] [ 3] ./a.out[0x408f54]
[arch:09670] [ 4] /usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7f422e9e9800]
[arch:09670] [ 5] ./a.out[0x408c19]
[arch:09670] *** End of error message ***
--------------------------------------------------------------------------
mpirun noticed that process rank 1 with PID 9670 on node arch exited on signal 11 (Segmentation fault).
--------------------------------------------------------------------------
这是代码
#include <opencv2/opencv.hpp>
#include <mpi.h>
int main(int argc, char **argv) {
cv::Mat_<uint> img(640,480);
cv::Mat_<uint> gray(640,480);
cv::VideoCapture cam(0);
int rank, nproc, j=0;
MPI_Status status;
MPI_Init(&argc, &argv);
// MPI datatype for 8UC3 image
MPI_Datatype mat_8uc3;
MPI_Type_contiguous(sizeof(img), MPI_BYTE, &mat_8uc3);
MPI_Type_commit(&mat_8uc3);
// MPI datatype for 8UC1 image
MPI_Datatype mat_8uc1;
MPI_Type_contiguous(sizeof(gray), MPI_BYTE, &mat_8uc1);
MPI_Type_commit(&mat_8uc1);
MPI_Comm_size(MPI_COMM_WORLD, &nproc); // number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // rank of the current process
/*
* Thread 0 captures the image from camera
* and sends the image to process 1 for processing
* thread 1 converts the image to grayscale and
* displays the image
*/
if (rank == 0) {
// capture the image and send to thread 1
while (1) {
cam >> img;
cv::imshow("proc 0", img);
MPI_Send(&img, 1, mat_8uc3, 1, j, MPI_COMM_WORLD);
cv::waitKey(40);
j++;
}
}
else if (rank == 1) {
// receive the image, convert to grayscale and display
while (1) {
MPI_Recv(&img, 1, mat_8uc3, 0, j, MPI_COMM_WORLD, &status);
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::imshow("proc 1", gray);
cv::waitKey(20);
j++;
}
}
MPI_Finalize();
return 0;
}
谁能指出我哪里错了
谢谢
编辑:(在 user0815 的回答之后)
根据建议的更改,问题 Device or resource busy
已解决,但程序仍然出现段错误。
[arch:01080] *** Process received signal ***
[arch:01080] Signal: Segmentation fault (11)
[arch:01080] Signal code: Address not mapped (1)
[arch:01080] Failing at address: 0x16bbf80
[arch:01080] [ 0] /usr/lib/libpthread.so.0(+0x10740)[0x7fea97322740]
[arch:01080] [ 1] /usr/lib/libopencv_core.so.2.4(_ZNK2cv11_InputArray6getMatEi+0x203)[0x7fea9b302113]
[arch:01080] [ 2] /usr/lib/libopencv_imgproc.so.2.4(_ZN2cv8cvtColorERKNS_11_InputArrayERKNS_12_OutputArrayEii+0x50)[0x7fea9a29bde0]
[arch:01080] [ 3] ./a.out[0x408fc3]
[arch:01080] [ 4] /usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7fea9605f800]
[arch:01080] [ 5] ./a.out[0x408c79]
[arch:01080] *** End of error message ***
--------------------------------------------------------------------------
mpirun noticed that process rank 1 with PID 1080 on node arch exited on signal 11 (Segmentation fault).
--------------------------------------------------------------------------
目前每个进程都尝试打开相机。这很可能会导致问题。尝试将开口移动到根特定部分,如下所示:
int main(int argc, char **argv) {
cv::Mat_<uint> img(640,480);
cv::Mat_<uint> gray(640,480);
cv::VideoCapture cam;
/* ... */
if (rank == 0) {
cam.open(0);
/* ... */
}
/* ... */
}
更新:
我认为您的代码存在问题,您不能简单地使用 MPI_Send
传输对象。 sizeof
运算符通常对对象也无效。如果要传输对象,则需要传输底层数据。
您可以通过发送大小为 img.rows * img.cols * sizeof(uint)
的 img.data
来实现此目的。然后你也可以使用 MPI_BYTE 作为数据类型,不需要自定义类型。
关于cv::Mat_
内部结构的一些细节可以参考here。
正如@user0851所注意到的,在您的代码中,所有进程都尝试打开相机,并且相机的打开可以由根进程单独执行。
openCV的Mat
对象很复杂,定义对应的MPI_Datatype
可能也很复杂。相反,发送像素数组 img.data
要容易得多。这是一小段代码,演示了如何完成。它由 mpiCC main.cpp -o main -lopencv_highgui -lopencv_imgproc -lopencv_core
和 运行 由 mpirun -np 2 main
编译
#include <opencv2/opencv.hpp>
#include <mpi.h>
using namespace cv;
int main(int argc, char **argv) {
Mat img;
Mat gray;
int rank, nproc, j=0;
size_t total;
size_t elemsize;
int sizes[3];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nproc); // number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // rank of the current process
/*
* Thread 0 captures the image from camera
* and sends the image to process 1 for processing
* thread 1 converts the image to grayscale and
* displays the image
*/
if (rank == 0) {
VideoCapture cam(0);
if(!cam.isOpened()){
fprintf(stderr,"unable to open camera.\n");
exit(1);
}
// capture the image and send to thread 1
while (1) {
cam >> img;
cv::imshow("proc 0", img);
if(j==0){
sizes[2]=img.elemSize();
Size s = img.size();
sizes[0] = s.height;
sizes[1] = s.width;
MPI_Send( sizes, 3, MPI_INT, 1,0, MPI_COMM_WORLD);
}
MPI_Send( img.data, sizes[0]*sizes[1]*3, MPI_CHAR,1,1, MPI_COMM_WORLD);
cv::waitKey(40);
j++;
}
}
else if (rank == 1) {
// receive the image, convert to grayscale and display
while (1) {
if(j==0){
MPI_Recv( sizes,3, MPI_INT,0,0, MPI_COMM_WORLD,&status);
img.create(sizes[0],sizes[1],CV_8UC3);
}
MPI_Recv( img.data, sizes[0]*sizes[1]*3, MPI_CHAR,0,1, MPI_COMM_WORLD,&status);
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::imshow("proc 1", gray);
cv::waitKey(20);
j++;
}
}
MPI_Finalize();
return 0;
}
Mat object只是一个header结构,指向存储图像的内存。
所以你有一些问题:
首先,您创建一个 Mat
object 大小的 640X640,然后从相机读取到 object。但是 Mat
只是一个 header 它不是指向数据的指针,Mat object 现在可以是任何宽度和高度。
其次,sizeof(Mat)
不return分配给图像的内存量,只有Mat object本身的内存量。图像需要的内存量是Mat.total()*Mat.elemSize()
我正在尝试用 C++ 学习 MPI。我对 OpenCV 有一些了解,所以我尝试使用 MPI 和 OpenCV 编写程序。这听起来可能很愚蠢,但出于学习的目的,我尝试从线程 0 上的网络摄像头捕获图像并将图像传递给线程 1 以转换为灰度并显示灰度图像。
这是我编译代码的方式:
mpic++ opencv.cpp `pkg-config opencv --libs`
代码编译成功,但是当我 运行 可执行文件时,屏幕上显示了一个图像几分之一秒,这就是我在终端上看到的
~/mpi$ mpirun -np 2 ./a.out
libv4l2: error setting pixformat: Device or resource busy
HIGHGUI ERROR: libv4l unable to ioctl S_FMT
libv4l2: error setting pixformat: Device or resource busy
libv4l1: error setting pixformat: Device or resource busy
HIGHGUI ERROR: libv4l unable to ioctl VIDIOCSPICT
[arch:09670] *** Process received signal ***
[arch:09670] Signal: Segmentation fault (11)
[arch:09670] Signal code: Address not mapped (1)
[arch:09670] Failing at address: 0x218ac50
[arch:09670] [ 0] /usr/lib/libpthread.so.0(+0x10740)[0x7f422fcac740]
[arch:09670] [ 1] /usr/lib/libopencv_core.so.2.4(_ZNK2cv11_InputArray6getMatEi+0x203)[0x7f4233c8c113]
[arch:09670] [ 2] /usr/lib/libopencv_imgproc.so.2.4(_ZN2cv8cvtColorERKNS_11_InputArrayERKNS_12_OutputArrayEii+0x50)[0x7f4232c25de0]
[arch:09670] [ 3] ./a.out[0x408f54]
[arch:09670] [ 4] /usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7f422e9e9800]
[arch:09670] [ 5] ./a.out[0x408c19]
[arch:09670] *** End of error message ***
--------------------------------------------------------------------------
mpirun noticed that process rank 1 with PID 9670 on node arch exited on signal 11 (Segmentation fault).
--------------------------------------------------------------------------
这是代码
#include <opencv2/opencv.hpp>
#include <mpi.h>
int main(int argc, char **argv) {
cv::Mat_<uint> img(640,480);
cv::Mat_<uint> gray(640,480);
cv::VideoCapture cam(0);
int rank, nproc, j=0;
MPI_Status status;
MPI_Init(&argc, &argv);
// MPI datatype for 8UC3 image
MPI_Datatype mat_8uc3;
MPI_Type_contiguous(sizeof(img), MPI_BYTE, &mat_8uc3);
MPI_Type_commit(&mat_8uc3);
// MPI datatype for 8UC1 image
MPI_Datatype mat_8uc1;
MPI_Type_contiguous(sizeof(gray), MPI_BYTE, &mat_8uc1);
MPI_Type_commit(&mat_8uc1);
MPI_Comm_size(MPI_COMM_WORLD, &nproc); // number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // rank of the current process
/*
* Thread 0 captures the image from camera
* and sends the image to process 1 for processing
* thread 1 converts the image to grayscale and
* displays the image
*/
if (rank == 0) {
// capture the image and send to thread 1
while (1) {
cam >> img;
cv::imshow("proc 0", img);
MPI_Send(&img, 1, mat_8uc3, 1, j, MPI_COMM_WORLD);
cv::waitKey(40);
j++;
}
}
else if (rank == 1) {
// receive the image, convert to grayscale and display
while (1) {
MPI_Recv(&img, 1, mat_8uc3, 0, j, MPI_COMM_WORLD, &status);
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::imshow("proc 1", gray);
cv::waitKey(20);
j++;
}
}
MPI_Finalize();
return 0;
}
谁能指出我哪里错了
谢谢
编辑:(在 user0815 的回答之后)
根据建议的更改,问题 Device or resource busy
已解决,但程序仍然出现段错误。
[arch:01080] *** Process received signal ***
[arch:01080] Signal: Segmentation fault (11)
[arch:01080] Signal code: Address not mapped (1)
[arch:01080] Failing at address: 0x16bbf80
[arch:01080] [ 0] /usr/lib/libpthread.so.0(+0x10740)[0x7fea97322740]
[arch:01080] [ 1] /usr/lib/libopencv_core.so.2.4(_ZNK2cv11_InputArray6getMatEi+0x203)[0x7fea9b302113]
[arch:01080] [ 2] /usr/lib/libopencv_imgproc.so.2.4(_ZN2cv8cvtColorERKNS_11_InputArrayERKNS_12_OutputArrayEii+0x50)[0x7fea9a29bde0]
[arch:01080] [ 3] ./a.out[0x408fc3]
[arch:01080] [ 4] /usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7fea9605f800]
[arch:01080] [ 5] ./a.out[0x408c79]
[arch:01080] *** End of error message ***
--------------------------------------------------------------------------
mpirun noticed that process rank 1 with PID 1080 on node arch exited on signal 11 (Segmentation fault).
--------------------------------------------------------------------------
目前每个进程都尝试打开相机。这很可能会导致问题。尝试将开口移动到根特定部分,如下所示:
int main(int argc, char **argv) {
cv::Mat_<uint> img(640,480);
cv::Mat_<uint> gray(640,480);
cv::VideoCapture cam;
/* ... */
if (rank == 0) {
cam.open(0);
/* ... */
}
/* ... */
}
更新:
我认为您的代码存在问题,您不能简单地使用 MPI_Send
传输对象。 sizeof
运算符通常对对象也无效。如果要传输对象,则需要传输底层数据。
您可以通过发送大小为 img.rows * img.cols * sizeof(uint)
的 img.data
来实现此目的。然后你也可以使用 MPI_BYTE 作为数据类型,不需要自定义类型。
关于cv::Mat_
内部结构的一些细节可以参考here。
正如@user0851所注意到的,在您的代码中,所有进程都尝试打开相机,并且相机的打开可以由根进程单独执行。
openCV的Mat
对象很复杂,定义对应的MPI_Datatype
可能也很复杂。相反,发送像素数组 img.data
要容易得多。这是一小段代码,演示了如何完成。它由 mpiCC main.cpp -o main -lopencv_highgui -lopencv_imgproc -lopencv_core
和 运行 由 mpirun -np 2 main
#include <opencv2/opencv.hpp>
#include <mpi.h>
using namespace cv;
int main(int argc, char **argv) {
Mat img;
Mat gray;
int rank, nproc, j=0;
size_t total;
size_t elemsize;
int sizes[3];
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &nproc); // number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // rank of the current process
/*
* Thread 0 captures the image from camera
* and sends the image to process 1 for processing
* thread 1 converts the image to grayscale and
* displays the image
*/
if (rank == 0) {
VideoCapture cam(0);
if(!cam.isOpened()){
fprintf(stderr,"unable to open camera.\n");
exit(1);
}
// capture the image and send to thread 1
while (1) {
cam >> img;
cv::imshow("proc 0", img);
if(j==0){
sizes[2]=img.elemSize();
Size s = img.size();
sizes[0] = s.height;
sizes[1] = s.width;
MPI_Send( sizes, 3, MPI_INT, 1,0, MPI_COMM_WORLD);
}
MPI_Send( img.data, sizes[0]*sizes[1]*3, MPI_CHAR,1,1, MPI_COMM_WORLD);
cv::waitKey(40);
j++;
}
}
else if (rank == 1) {
// receive the image, convert to grayscale and display
while (1) {
if(j==0){
MPI_Recv( sizes,3, MPI_INT,0,0, MPI_COMM_WORLD,&status);
img.create(sizes[0],sizes[1],CV_8UC3);
}
MPI_Recv( img.data, sizes[0]*sizes[1]*3, MPI_CHAR,0,1, MPI_COMM_WORLD,&status);
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::imshow("proc 1", gray);
cv::waitKey(20);
j++;
}
}
MPI_Finalize();
return 0;
}
Mat object只是一个header结构,指向存储图像的内存。
所以你有一些问题:
首先,您创建一个 Mat
object 大小的 640X640,然后从相机读取到 object。但是 Mat
只是一个 header 它不是指向数据的指针,Mat object 现在可以是任何宽度和高度。
其次,sizeof(Mat)
不return分配给图像的内存量,只有Mat object本身的内存量。图像需要的内存量是Mat.total()*Mat.elemSize()