OpenCV矩阵转换为共享内存

3
我希望能够在两个Linux进程之间共享CvMat对象(OpenCV库中的矩阵),为此我使用了共享内存。一个进程(服务器)将从网络摄像头中捕获一帧(矩阵),将其转换为灰度图像,使用共享内存共享它,并在屏幕上显示该帧。另一个进程(客户端)将读取共享帧并执行一些操作。请参见下面的代码。
问题似乎是客户端没有读取信息,因为“行”和“列”都为零(或者服务器没有写入共享内存)。无论如何,我没有收到任何错误消息,也不知道自己做错了什么。有什么想法吗?
非常感谢!
以下是服务器代码:
#include <iostream>
#include <cv.h>
#include <highgui.h>
using namespace std;

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

#include "2cam.h"

int sizeofmat(CvMat *mat) {
    return mat->rows * mat->cols * CV_ELEM_SIZE(mat->type);
}

int main() {
    int shmid;
    key_t key = 5678;

    CvMat *vdisp = cvCreateMat(240, 320, CV_8U);
    const size_t vdispsize = sizeofmat(vdisp);
    CvMat *s = cvCreateMat(240, 320, CV_8U);
    CvMat stub;
    CvSize imageSize = cvSize(320, 240);

    IplImage *color = cvCreateImage(imageSize, 8, 3);
    IplImage *gray = cvCreateImage(imageSize, 8, 1);

    /* Create the segment */
    if ((shmid = shmget(key, vdispsize, IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    /* Attach the segment to our data space */
    if ((vdisp = (CvMat *)shmat(shmid, NULL, 0)) == (CvMat *)-1) {
        perror("shmat");
        exit(1);
    }

    /* Put CvMat into the memory to be read for other processes */
    s = vdisp;

    /* Create camera */
    Camera c("/dev/video0", 320, 240, 30);

    while (1) {
        /* Get one frame */
        c.Update();
        c.toIplImage(color);

        /* Convert color frame to grayscale */
        cvCvtColor(color, gray, CV_BGR2GRAY);

        /* Get matrix from the gray frame and write the matrix in shared memory*/
        s = cvGetMat(gray, &stub, 0, 0);

        /* Show frame */
        cvNamedWindow("result", CV_WINDOW_AUTOSIZE);
        cvShowImage("result", s);

        /* Wait for escape key */
        if ((cvWaitKey(10) & 255) == 27)
            break;
    }

    /* free memory */
    cvDestroyWindow("result");
    cvReleaseImage(&color);
    cvReleaseImage(&gray);
    //cvReleaseMat(&vdisp);
    //cvReleaseMat(&s);

    return 0;
}  

以下是客户端的代码:

#include <iostream>
#include <cv.h>
#include <highgui.h>
using namespace std;

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

int sizeofmat(CvMat *mat) {
    return mat->rows * mat->cols * CV_ELEM_SIZE(mat->type);
}

int main() {
    int shmid;
    key_t key = 5678;

    CvMat *vdisp = cvCreateMat(240, 320, CV_8U);
    const size_t vdispsize = sizeofmat(vdisp);
    CvMat *s = cvCreateMat(240, 320, CV_8U);

    /* Locate the segment */
    if ((shmid = shmget(key, vdispsize, 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    /* Now we attach the segment to our data space */
    if ((vdisp = (CvMat *)shmat(shmid, NULL, 0)) == (CvMat *) -1) {
        perror("shmat");
        exit(1);
    }

    s = vdisp;

    cout << "rows: " << s->rows << endl;
    cout << "cols: " << s->cols << endl;

    return 0;
}

你不能使用线程吗? - karlphillip
没问题!但这是一个个人项目,所以我决定使用共享内存来学习它的工作原理。顺便说一下,谢谢你的建议。 - Victor Gaspar
如果你想要功能强大且快速的话,可以使用 gstreamer 的 shmsrcshmsink(OpenCV 示例:https://github.com/tik0/mat2gstreamer)。 - Tik0
2个回答

5

感谢larsmans指出了正确的方向。无论如何,我自己回答了这个问题,以防有人需要相同的解决方案。


这是服务器端的代码:

#include <iostream>
#include <cv.h>
#include <highgui.h>
using namespace std;

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

#include "2cam.h"

int sizeofmat(CvMat *mat) {
    return mat->rows * mat->step;
}

int main() {
    int shmid;
    key_t key = 5678;

    uchar *vdisp;
    CvMat *s = cvCreateMat(240, 320, CV_8U);
    CvMat *tmp = cvCreateMat(240, 320, CV_8U);
    const size_t vdispsize = sizeofmat(s);
    CvMat stub;
    CvSize imageSize = cvSize(320, 240);

    IplImage *color = cvCreateImage(imageSize, 8, 3);
    IplImage *gray = cvCreateImage(imageSize, 8, 1);

    /* Create the segment */
    if ((shmid = shmget(key, vdispsize, IPC_CREAT | 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    /* Attach the segment to our data space */
    if ((vdisp = (uchar *) shmat(shmid, NULL, 0)) == (uchar *) -1) {
        perror("shmat");
        exit(1);
    }

    s->data.ptr = vdisp;

    /* Create camera */
    Camera c("/dev/video0", 320, 240, 30);

    while (1) {
        /* Get one frame */
        c.Update();
        c.toIplImage(color);

        /* Convert color frame to grayscale */
        cvCvtColor(color, gray, CV_BGR2GRAY);

        /* Get matrix from the gray frame and write the matrix in shared memory*/
        tmp = cvGetMat(gray, &stub, 0, 0);

        for (int row = 0; row < tmp->rows; row++) {
            const uchar* ptr = (const uchar*) (tmp->data.ptr + row * tmp->step);
            memcpy((uchar*)(s->data.ptr + row * s->step), ptr, tmp->step);
        }

        /* Show frame */
        cvNamedWindow("result", CV_WINDOW_AUTOSIZE);
        cvShowImage("result", s);

        /* Wait for escape key */
        if ((cvWaitKey(10) & 255) == 27)
            break;
    }

    /* free memory */
    cvDestroyWindow("result");
    cvReleaseImage(&color);
    cvReleaseImage(&gray);

    return 0;
}

以下是客户端的代码:

#include <iostream>
#include <cv.h>
#include <highgui.h>
using namespace std;

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>

int sizeofmat(CvMat *mat) {
    return mat->rows * mat->step;
}

int main() {
    int shmid;
    key_t key = 5678;

    uchar *vdisp;
    CvMat *s = cvCreateMat(240, 320, CV_8U);
    const size_t vdispsize = sizeofmat(s);

    /* Locate the segment */
    if ((shmid = shmget(key, vdispsize, 0666)) < 0) {
        perror("shmget");
        exit(1);
    }

    /* Now we attach the segment to our data space */
    if ((vdisp = (uchar *)shmat(shmid, NULL, 0)) == (uchar *) -1) {
        perror("shmat");
        exit(1);
    }

    s->data.ptr = vdisp;

    cvNamedWindow("result", CV_WINDOW_AUTOSIZE);
    cvShowImage("result", s);

    cvWaitKey(10000);

    cvDestroyWindow("result");

    return 0;
}

好的,但如果发送的是三通道图像(非灰度图像),你该怎么做呢? - user1098761
我知道这很老,但根据shmctl手册页面,您必须清理使用shmget创建的任何shm段。(例如:shmctl(shmid, IPC_RMID, NULL)) - skaldesh

3
如果您正在使用Boost Interprocess,应该是这样的。
if(argv[1] = SERVER){

  struct shm_remove{
    shm_remove() {shared_memory_object::remove("SharedMemoryForCvMat"); }
    ~shm_remove(){shared_memory_object::remove("SharedMemoryForCvMat"); }
  }remover;

  shared_memory_object shm (create_only,"SharedMemoryForCvMat",read_write);
  shm.truncate(widht*height*channel);
  mapped_region region(shm, read_write);

  cv::VideoCapture cap(0);
  cv::Mat frame;

  while(everything_is_ok_for_server){
    cap >> frame;
    memcpy(region.get_address(), frame.data, width*height*channel);   
  }
}

else if(argv[1] == CLIENT){

    shared_memory_object shm (open_only, "SharedMemoryForCvMat", read_only);
    mapped_region region(shm, read_only);

    while(everything_is_ok_for_client){

        cv::Mat frame(cv::Size(width, height),
                      CV_8UC3,
                      region.get_address(),
                      cv::Mat::AUTO_STEP);
        cv::imshow("unimportant_string",frame);
        cv::waitKey(2);
    }
}

在这种情况下,添加一些头文件可能会有所帮助。 - Saeed Masoomi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接