カメラの画像を仮想デバイスに送り出す。 | sy-engのブログ

sy-engのブログ

システムを作りたい。
自分用の記録。

カメラの画像を処理してv4l2loopbackに放り込む方法。

いい加減、githubを使うべきな気もしている。

 

//////liboutstream.h/////

#ifdef __cplusplus
extern "C"{
#endif

  int writeData(int devfd, unsigned char* data, int length);
  int close_write_device(int devfd);
  int open_write_device(char* dev, int width, int height, int pixFormat);

#ifdef __cplusplus
}
#endif

#define PIX_FMT_RGB24 1
#define PIX_FMT_UYVY 2
#define PIX_FMT_BGR24 3
 

////liboutstream.c/////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>              /* low-level i/o */
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

#include <linux/videodev2.h>

#define PIX_FMT_RGB24 1
#define PIX_FMT_UYVY 2
#define PIX_FMT_BGR24 3

static void errno_exit(const char *s){
  fprintf(stderr, "%s error %d, %s\n",s, errno, strerror(errno));  
  exit(EXIT_FAILURE);
}

static int xioctl(int fd,int request,void *arg){
  int r;
  do{ r = ioctl(fd, request, arg); }
  while(-1 == r && EINTR == errno);
  return r;
}

int writeData(int devfd, __u8 * data, int length) {
  __u8 tmp;
  int i;

/*These codes may be for YUVU format.
* Now, main code uses RGB format.
* These codes are not required. */
/*
  for(i = 0; i < length; i = i + 2){
    tmp = *(data + i);
    *(data + i) = *(data + i + 1);
    *(data + i + 1) = tmp;
  }
*/

  int retValue = write(devfd, data, length);

  return retValue;
}

int close_write_device(int devfd) {
    close(devfd);
    return 0;
}

int open_write_device(char* dev, int width, int height, int pixFormat){
    struct v4l2_capability vid_caps;
    struct v4l2_format vid_format;

    memset(&vid_format, 0, sizeof(vid_format));
//    vid_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
    vid_format.fmt.pix.width = width;
    vid_format.fmt.pix.height = height;

    //printf("format : %d %d %d\n", pixFormat, vid_format.fmt.pix.width, vid_format.fmt.pix.height);

    switch (pixFormat) {
        case PIX_FMT_RGB24:
            vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
            vid_format.fmt.pix.bytesperline = (width * 3);
            vid_format.fmt.pix.sizeimage = (width * height * 3);
            printf("uuu %d\n", V4L2_PIX_FMT_RGB24);
            break;

        // UYVY has only half the color resolution of RGB, but there is less confusion
        // surrounding its byte ordering
        case PIX_FMT_UYVY:
            vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
            vid_format.fmt.pix.bytesperline = (width * 2);
            vid_format.fmt.pix.sizeimage = (width * height * 2);
            printf("ppp %d\n", V4L2_PIX_FMT_UYVY);
            break;

        // Adding BGR24 as format #3 in anticipation of Chrome hopefully adopting it in the future
        // (After all, BGR24 is what they actually implemented, they just call it RGB24)
        case PIX_FMT_BGR24:
            vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
            vid_format.fmt.pix.bytesperline = (width * 3);
            vid_format.fmt.pix.sizeimage = (width * height * 3);
            break;

        default:
            // Unknown pixFormat, bail out.
            return -1;
    }

    vid_format.fmt.pix.field = V4L2_FIELD_NONE;
    vid_format.fmt.pix.priv = 0;
    vid_format.fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;

    int fdwr = open(dev, O_RDWR, O_NONBLOCK);
    int ret_code = xioctl(fdwr, VIDIOC_QUERYCAP, &vid_caps);

    //printf("qqq %d\n", vid_format.fmt.pix.pixelformat);

    if(ret_code = xioctl(fdwr, VIDIOC_S_FMT, &vid_format) == -1){
        errno_exit("VIDIOC_S_FMT");
    }
    //printf("vvv %d %d %d\n", fdwr, ret_code, vid_format.fmt.pix.pixelformat);
    fflush(stdout);
    return fdwr;

}
 

/////main.cpp/////

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp> // highguiのヘッダーをインクルード
#include <iostream>
#include <webcam.h>
#include "liboutstream.h"
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;
using namespace std;

#define WRITE_DEV_NAME "/dev/video7"

int main(int, char**){
  CResult ret;
  CHandle handle;
  CControlValue value;
  bool loop = true;
  int write_fd;
  Mat frame, edge, result;

  VideoCapture cap(0); // デフォルトカメラをオープン
  if(!cap.isOpened()){  // 成功したかどうかをチェック
    return -1;
  }
  //cap.set(CAP_PROP_FRAME_WIDTH, 1920); //too large
  //cap.set(CAP_PROP_FRAME_HEIGHT, 1080); //too large
  //cap.set(CAP_PROP_FPS, 5); //for 1920 x 1080

  //Mat edges;
  namedWindow("frame",1);

  //initialize webcam
  ret = c_init();
  if(ret){
    cout << "Error : c_init() : " << ret << "\n";
    loop = false;
    return 1;
  }

  handle = c_open_device("video0");
  if(!handle){
    cout << "Error : c_open_device() : " << ret << "\n";
    c_cleanup();
    return 1;
  }

  ret = c_get_control(handle, CC_AUTO_FOCUS, &value);
  value.value = 0;
  if(ret){
    cout << "Error : c_get_control() (init) : " << ret << "\n";
    loop = false;
    return 1;
  }

  ret = c_set_control(handle, CC_AUTO_FOCUS, &value);
  if(ret){
    cout << "Error : c_set_control() (init): " << ret << "\n";
    loop = false;
    return 1;
  }

  cap >> frame;

  cout << frame.cols << " " << frame.rows << "!!!\n\n";
  write_fd = open_write_device(WRITE_DEV_NAME, frame.cols, frame.rows, PIX_FMT_RGB24); //PIX_FMT_UYVY);

  while(loop){
    cap >> frame; // カメラから新しいフレームを取得
    cvtColor(frame, edge, COLOR_RGB2GRAY);
    GaussianBlur(edge, edge, Size(7,7), 1.5, 1.5);
    Canny(edge, edge, 0, 15, 3);
    
    cvtColor(edge, result, COLOR_GRAY2RGB);
    imshow("frame", result);
    writeData(write_fd, result.data, result.cols * result.rows * 3);
    
    switch(waitKey(30)){
      //Logicool C615 focus has 17 values, 0, 17, 34, 51, , , 238, 255
    case 'p':
      c_get_control(handle, CC_FOCUS_ABSOLUTE, &value);
      value.value += 17;
      c_set_control(handle, CC_FOCUS_ABSOLUTE, &value);
      ret = c_get_control(handle, CC_FOCUS_ABSOLUTE, &value);
      cout << value.value << "\n";
      break;
    case 'm':
      c_get_control(handle, CC_FOCUS_ABSOLUTE, &value);
      value.value -= 17;
      c_set_control(handle, CC_FOCUS_ABSOLUTE, &value);
      ret = c_get_control(handle, CC_FOCUS_ABSOLUTE, &value);
      cout << value.value << "\n";
      break;
    case 'e':
      loop = false;
      break;
    default:
      break;
    }
  }

  close_write_device(write_fd);
  c_close_device(handle);
  c_cleanup();

  // VideoCapture デストラクタにより,カメラは自動的に終了処理されます
  return 0;
}

 

/////Makefile/////

CC = g++
CFLAGS = 
CFLAGS_TMP = `pkg-config opencv --cflags` `pkg-config opencv --libs`
LDFLAGS = -L./ -L/usr/local/lib
INCLUDE = -I./ -I/usr/local/include/opencv4 -I/usr/include  -I/usr/local/include
LIBS = -lopencv_core -lopencv_videoio -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc -lm -lwebcam -loutstream

main: main.o
    $(CC) $(CFLAGS) -o $@ $< $(INCLUDE) $(LDFLAGS) $(LIBS)
    rm *.o
    rm *.a
#    rm *~

.cpp.o:
    gcc -c liboutstream.c -o liboutstream.o
    ar r liboutstream.a liboutstream.o
    g++ -o $@ -c $< $(INCLUDE)