parent
3fa73442cc
commit
a004735b19
19 changed files with 605 additions and 86 deletions
@ -0,0 +1,269 @@ |
|||||||
|
#include "camera_webcam.h" |
||||||
|
|
||||||
|
#include <unistd.h> |
||||||
|
#include <string.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <pthread.h> |
||||||
|
|
||||||
|
#include "common/util.h" |
||||||
|
#include "common/timing.h" |
||||||
|
#include "common/swaglog.h" |
||||||
|
#include "buffering.h" |
||||||
|
|
||||||
|
#include <opencv2/opencv.hpp> |
||||||
|
#include <opencv2/highgui.hpp> |
||||||
|
#include <opencv2/core.hpp> |
||||||
|
#include <opencv2/videoio.hpp> |
||||||
|
|
||||||
|
extern volatile sig_atomic_t do_exit; |
||||||
|
|
||||||
|
#define FRAME_WIDTH 1164 |
||||||
|
#define FRAME_HEIGHT 874 |
||||||
|
#define FRAME_WIDTH_FRONT 1152 |
||||||
|
#define FRAME_HEIGHT_FRONT 864 |
||||||
|
|
||||||
|
namespace { |
||||||
|
void camera_open(CameraState *s, VisionBuf *camera_bufs, bool rear) { |
||||||
|
assert(camera_bufs); |
||||||
|
s->camera_bufs = camera_bufs; |
||||||
|
} |
||||||
|
|
||||||
|
void camera_close(CameraState *s) { |
||||||
|
tbuffer_stop(&s->camera_tb); |
||||||
|
} |
||||||
|
|
||||||
|
void camera_release_buffer(void *cookie, int buf_idx) { |
||||||
|
CameraState *s = static_cast<CameraState *>(cookie); |
||||||
|
} |
||||||
|
|
||||||
|
void camera_init(CameraState *s, int camera_id, unsigned int fps) { |
||||||
|
assert(camera_id < ARRAYSIZE(cameras_supported)); |
||||||
|
s->ci = cameras_supported[camera_id]; |
||||||
|
assert(s->ci.frame_width != 0); |
||||||
|
|
||||||
|
s->frame_size = s->ci.frame_height * s->ci.frame_stride; |
||||||
|
s->fps = fps; |
||||||
|
|
||||||
|
tbuffer_init2(&s->camera_tb, FRAME_BUF_COUNT, "frame", camera_release_buffer, s); |
||||||
|
} |
||||||
|
|
||||||
|
static void* rear_thread(void *arg) { |
||||||
|
int err; |
||||||
|
|
||||||
|
set_thread_name("webcam_rear_thread"); |
||||||
|
CameraState* s = (CameraState*)arg; |
||||||
|
|
||||||
|
cv::VideoCapture cap_rear(1); // road
|
||||||
|
cap_rear.set(cv::CAP_PROP_FRAME_WIDTH, 1280); |
||||||
|
cap_rear.set(cv::CAP_PROP_FRAME_HEIGHT, 720); |
||||||
|
cap_rear.set(cv::CAP_PROP_FPS, s->fps); |
||||||
|
cap_rear.set(cv::CAP_PROP_AUTOFOCUS, 0); // off
|
||||||
|
cap_rear.set(cv::CAP_PROP_FOCUS, 0); // 0 - 255?
|
||||||
|
// cv::Rect roi_rear(160, 0, 960, 720);
|
||||||
|
|
||||||
|
cv::Size size; |
||||||
|
size.height = s->ci.frame_height; |
||||||
|
size.width = s->ci.frame_width; |
||||||
|
|
||||||
|
// transforms calculation see tools/webcam/warp_vis.py
|
||||||
|
float ts[9] = {1.00220264, 0.0, -59.40969163,
|
||||||
|
0.0, 1.00220264, 76.20704846,
|
||||||
|
0.0, 0.0, 1.0}; |
||||||
|
const cv::Mat transform = cv::Mat(3, 3, CV_32F, ts); |
||||||
|
|
||||||
|
if (!cap_rear.isOpened()) { |
||||||
|
err = 1; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t frame_id = 0; |
||||||
|
TBuffer* tb = &s->camera_tb; |
||||||
|
|
||||||
|
while (!do_exit) { |
||||||
|
cv::Mat frame_mat; |
||||||
|
cv::Mat transformed_mat; |
||||||
|
|
||||||
|
cap_rear >> frame_mat; |
||||||
|
|
||||||
|
// int rows = frame_mat.rows;
|
||||||
|
// int cols = frame_mat.cols;
|
||||||
|
// printf("Raw Rear, R=%d, C=%d\n", rows, cols);
|
||||||
|
|
||||||
|
cv::warpPerspective(frame_mat, transformed_mat, transform, size, cv::INTER_LINEAR, cv::BORDER_CONSTANT, 0); |
||||||
|
|
||||||
|
int transformed_size = transformed_mat.total() * transformed_mat.elemSize(); |
||||||
|
|
||||||
|
const int buf_idx = tbuffer_select(tb); |
||||||
|
s->camera_bufs_metadata[buf_idx] = { |
||||||
|
.frame_id = frame_id, |
||||||
|
}; |
||||||
|
|
||||||
|
cl_command_queue q = s->camera_bufs[buf_idx].copy_q; |
||||||
|
cl_mem yuv_cl = s->camera_bufs[buf_idx].buf_cl; |
||||||
|
cl_event map_event; |
||||||
|
void *yuv_buf = (void *)clEnqueueMapBuffer(q, yuv_cl, CL_TRUE, |
||||||
|
CL_MAP_WRITE, 0, transformed_size, |
||||||
|
0, NULL, &map_event, &err); |
||||||
|
assert(err == 0); |
||||||
|
clWaitForEvents(1, &map_event); |
||||||
|
clReleaseEvent(map_event); |
||||||
|
memcpy(yuv_buf, transformed_mat.data, transformed_size); |
||||||
|
|
||||||
|
clEnqueueUnmapMemObject(q, yuv_cl, yuv_buf, 0, NULL, &map_event); |
||||||
|
clWaitForEvents(1, &map_event); |
||||||
|
clReleaseEvent(map_event); |
||||||
|
tbuffer_dispatch(tb, buf_idx); |
||||||
|
|
||||||
|
frame_id += 1; |
||||||
|
frame_mat.release(); |
||||||
|
transformed_mat.release(); |
||||||
|
} |
||||||
|
|
||||||
|
cap_rear.release(); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void front_thread(CameraState *s) { |
||||||
|
int err; |
||||||
|
|
||||||
|
cv::VideoCapture cap_front(2); // driver
|
||||||
|
cap_front.set(cv::CAP_PROP_FRAME_WIDTH, 1280); |
||||||
|
cap_front.set(cv::CAP_PROP_FRAME_HEIGHT, 720); |
||||||
|
cap_front.set(cv::CAP_PROP_FPS, s->fps); |
||||||
|
// cv::Rect roi_front(320, 0, 960, 720);
|
||||||
|
|
||||||
|
cv::Size size; |
||||||
|
size.height = s->ci.frame_height; |
||||||
|
size.width = s->ci.frame_width; |
||||||
|
|
||||||
|
// transforms calculation see tools/webcam/warp_vis.py
|
||||||
|
float ts[9] = {0.94713656, 0.0, -30.16740088,
|
||||||
|
0.0, 0.94713656, 91.030837,
|
||||||
|
0.0, 0.0, 1.0}; |
||||||
|
const cv::Mat transform = cv::Mat(3, 3, CV_32F, ts); |
||||||
|
|
||||||
|
if (!cap_front.isOpened()) { |
||||||
|
err = 1; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t frame_id = 0; |
||||||
|
TBuffer* tb = &s->camera_tb; |
||||||
|
|
||||||
|
while (!do_exit) { |
||||||
|
cv::Mat frame_mat; |
||||||
|
cv::Mat transformed_mat; |
||||||
|
|
||||||
|
cap_front >> frame_mat; |
||||||
|
|
||||||
|
// int rows = frame_mat.rows;
|
||||||
|
// int cols = frame_mat.cols;
|
||||||
|
// printf("Raw Front, R=%d, C=%d\n", rows, cols);
|
||||||
|
|
||||||
|
cv::warpPerspective(frame_mat, transformed_mat, transform, size, cv::INTER_LINEAR, cv::BORDER_CONSTANT, 0); |
||||||
|
|
||||||
|
int transformed_size = transformed_mat.total() * transformed_mat.elemSize(); |
||||||
|
|
||||||
|
const int buf_idx = tbuffer_select(tb); |
||||||
|
s->camera_bufs_metadata[buf_idx] = { |
||||||
|
.frame_id = frame_id, |
||||||
|
}; |
||||||
|
|
||||||
|
cl_command_queue q = s->camera_bufs[buf_idx].copy_q; |
||||||
|
cl_mem yuv_cl = s->camera_bufs[buf_idx].buf_cl; |
||||||
|
cl_event map_event; |
||||||
|
void *yuv_buf = (void *)clEnqueueMapBuffer(q, yuv_cl, CL_TRUE, |
||||||
|
CL_MAP_WRITE, 0, transformed_size, |
||||||
|
0, NULL, &map_event, &err); |
||||||
|
assert(err == 0); |
||||||
|
clWaitForEvents(1, &map_event); |
||||||
|
clReleaseEvent(map_event); |
||||||
|
memcpy(yuv_buf, transformed_mat.data, transformed_size); |
||||||
|
|
||||||
|
clEnqueueUnmapMemObject(q, yuv_cl, yuv_buf, 0, NULL, &map_event); |
||||||
|
clWaitForEvents(1, &map_event); |
||||||
|
clReleaseEvent(map_event); |
||||||
|
tbuffer_dispatch(tb, buf_idx); |
||||||
|
|
||||||
|
frame_id += 1; |
||||||
|
frame_mat.release(); |
||||||
|
transformed_mat.release(); |
||||||
|
} |
||||||
|
|
||||||
|
cap_front.release(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
CameraInfo cameras_supported[CAMERA_ID_MAX] = { |
||||||
|
// road facing
|
||||||
|
[CAMERA_ID_LGC920] = { |
||||||
|
.frame_width = FRAME_WIDTH, |
||||||
|
.frame_height = FRAME_HEIGHT, |
||||||
|
.frame_stride = FRAME_WIDTH*3, |
||||||
|
.bayer = false, |
||||||
|
.bayer_flip = false, |
||||||
|
}, |
||||||
|
// driver facing
|
||||||
|
[CAMERA_ID_LGC615] = { |
||||||
|
.frame_width = FRAME_WIDTH_FRONT, |
||||||
|
.frame_height = FRAME_HEIGHT_FRONT, |
||||||
|
.frame_stride = FRAME_WIDTH_FRONT*3, |
||||||
|
.bayer = false, |
||||||
|
.bayer_flip = false, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
void cameras_init(DualCameraState *s) { |
||||||
|
memset(s, 0, sizeof(*s)); |
||||||
|
|
||||||
|
camera_init(&s->rear, CAMERA_ID_LGC920, 20); |
||||||
|
s->rear.transform = (mat3){{ |
||||||
|
1.0, 0.0, 0.0, |
||||||
|
0.0, 1.0, 0.0, |
||||||
|
0.0, 0.0, 1.0, |
||||||
|
}}; |
||||||
|
|
||||||
|
camera_init(&s->front, CAMERA_ID_LGC615, 10); |
||||||
|
s->front.transform = (mat3){{ |
||||||
|
1.0, 0.0, 0.0, |
||||||
|
0.0, 1.0, 0.0, |
||||||
|
0.0, 0.0, 1.0, |
||||||
|
}}; |
||||||
|
} |
||||||
|
|
||||||
|
void camera_autoexposure(CameraState *s, float grey_frac) {} |
||||||
|
|
||||||
|
void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, |
||||||
|
VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, |
||||||
|
VisionBuf *camera_bufs_front) { |
||||||
|
assert(camera_bufs_rear); |
||||||
|
assert(camera_bufs_front); |
||||||
|
int err; |
||||||
|
|
||||||
|
// LOG("*** open front ***");
|
||||||
|
camera_open(&s->front, camera_bufs_front, false); |
||||||
|
|
||||||
|
// LOG("*** open rear ***");
|
||||||
|
camera_open(&s->rear, camera_bufs_rear, true); |
||||||
|
} |
||||||
|
|
||||||
|
void cameras_close(DualCameraState *s) { |
||||||
|
camera_close(&s->rear); |
||||||
|
camera_close(&s->front); |
||||||
|
} |
||||||
|
|
||||||
|
void cameras_run(DualCameraState *s) { |
||||||
|
set_thread_name("webcam_thread"); |
||||||
|
|
||||||
|
int err; |
||||||
|
pthread_t rear_thread_handle; |
||||||
|
err = pthread_create(&rear_thread_handle, NULL, |
||||||
|
rear_thread, &s->rear); |
||||||
|
assert(err == 0); |
||||||
|
|
||||||
|
front_thread(&s->front); |
||||||
|
|
||||||
|
err = pthread_join(rear_thread_handle, NULL); |
||||||
|
assert(err == 0); |
||||||
|
cameras_close(s); |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
#ifndef CAMERA_WEBCAM |
||||||
|
#define CAMERA_WEBCAM |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
#ifdef __APPLE__ |
||||||
|
#include <OpenCL/cl.h> |
||||||
|
#else |
||||||
|
#include <CL/cl.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "common/mat.h" |
||||||
|
|
||||||
|
#include "buffering.h" |
||||||
|
#include "common/visionbuf.h" |
||||||
|
#include "camera_common.h" |
||||||
|
|
||||||
|
#define FRAME_BUF_COUNT 16 |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef struct CameraState { |
||||||
|
int camera_id; |
||||||
|
CameraInfo ci; |
||||||
|
int frame_size; |
||||||
|
|
||||||
|
VisionBuf *camera_bufs; |
||||||
|
FrameMetadata camera_bufs_metadata[FRAME_BUF_COUNT]; |
||||||
|
TBuffer camera_tb; |
||||||
|
|
||||||
|
int fps; |
||||||
|
float digital_gain; |
||||||
|
|
||||||
|
float cur_gain_frac; |
||||||
|
|
||||||
|
mat3 transform; |
||||||
|
} CameraState; |
||||||
|
|
||||||
|
|
||||||
|
typedef struct DualCameraState { |
||||||
|
int ispif_fd; |
||||||
|
|
||||||
|
CameraState rear; |
||||||
|
CameraState front; |
||||||
|
} DualCameraState; |
||||||
|
|
||||||
|
void cameras_init(DualCameraState *s); |
||||||
|
void cameras_open(DualCameraState *s, VisionBuf *camera_bufs_rear, VisionBuf *camera_bufs_focus, VisionBuf *camera_bufs_stats, VisionBuf *camera_bufs_front); |
||||||
|
void cameras_run(DualCameraState *s); |
||||||
|
void cameras_close(DualCameraState *s); |
||||||
|
void camera_autoexposure(CameraState *s, float grey_frac); |
||||||
|
#ifdef __cplusplus |
||||||
|
} // extern "C"
|
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,43 @@ |
|||||||
|
Run openpilot with webcam on PC/laptop |
||||||
|
===================== |
||||||
|
What's needed: |
||||||
|
- Ubuntu 16.04 |
||||||
|
- Python 3.7.3 |
||||||
|
- GPU (recommended) |
||||||
|
- Two USB webcams, at least 720p and 78 degrees FOV (e.g. Logitech C920/C615) |
||||||
|
- [Car harness](https://comma.ai/shop/products/comma-car-harness) w/ black panda (or the outdated grey panda/giraffe combo) to connect to your car |
||||||
|
- [Panda paw](https://comma.ai/shop/products/panda-paw) (or USB-A to USB-A cable) to connect panda to your computer |
||||||
|
- Tape, Charger, ... |
||||||
|
That's it! |
||||||
|
|
||||||
|
## Clone openpilot and install the requirements |
||||||
|
``` |
||||||
|
cd ~ |
||||||
|
git clone https://github.com/commaai/openpilot.git |
||||||
|
# Follow [this readme](https://github.com/commaai/openpilot/tree/master/tools) to install the requirements |
||||||
|
# Add line "export PYTHONPATH=$HOME/openpilot" to your ~/.bashrc |
||||||
|
# You may also need to install tensorflow-gpu 2.0.0 |
||||||
|
# Install [OpenCV4](https://www.pyimagesearch.com/2018/08/15/how-to-install-opencv-4-on-ubuntu/) |
||||||
|
``` |
||||||
|
## Build openpilot for webcam |
||||||
|
``` |
||||||
|
cd ~/openpilot |
||||||
|
scons use_webcam=1 |
||||||
|
touch prebuilt |
||||||
|
``` |
||||||
|
## Connect the hardwares |
||||||
|
``` |
||||||
|
# Connect the road facing camera first, then the driver facing camera |
||||||
|
# (default indexes are 1 and 2; can be modified in selfdrive/camerad/cameras/camera_webcam.cc) |
||||||
|
# Connect your computer to panda |
||||||
|
``` |
||||||
|
## GO |
||||||
|
``` |
||||||
|
cd ~/openpilot/tools/webcam |
||||||
|
./accept_terms.py # accept the user terms so that thermald can detect the car started |
||||||
|
cd ~/openpilot/selfdrive |
||||||
|
PASSIVE=0 NOSENSOR=1 ./manager.py |
||||||
|
# Start the car, then the UI should show the road webcam's view |
||||||
|
# Adjust and secure the webcams (you can run tools/webcam/front_mount_helper.py to help mount the driver camera) |
||||||
|
# Finish calibration and engage! |
||||||
|
``` |
@ -0,0 +1,9 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
from common.params import Params |
||||||
|
from selfdrive.version import terms_version, training_version |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
params = Params() |
||||||
|
params.put("HasAcceptedTerms", str(terms_version, 'utf-8')) |
||||||
|
params.put("CompletedTrainingVersion", str(training_version, 'utf-8')) |
||||||
|
print("Terms Accepted!") |
@ -0,0 +1,35 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
import numpy as np |
||||||
|
|
||||||
|
# copied from common.transformations/camera.py |
||||||
|
eon_dcam_focal_length = 860.0 # pixels |
||||||
|
webcam_focal_length = 908.0 # pixels |
||||||
|
|
||||||
|
eon_dcam_intrinsics = np.array([ |
||||||
|
[eon_dcam_focal_length, 0, 1152/2.], |
||||||
|
[ 0, eon_dcam_focal_length, 864/2.], |
||||||
|
[ 0, 0, 1]]) |
||||||
|
|
||||||
|
webcam_intrinsics = np.array([ |
||||||
|
[webcam_focal_length, 0., 1280/2.], |
||||||
|
[ 0., webcam_focal_length, 720/2.], |
||||||
|
[ 0., 0., 1.]]) |
||||||
|
|
||||||
|
cam_id = 2 |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
import cv2 |
||||||
|
|
||||||
|
trans_webcam_to_eon_front = np.dot(eon_dcam_intrinsics,np.linalg.inv(webcam_intrinsics)) |
||||||
|
|
||||||
|
cap = cv2.VideoCapture(cam_id) |
||||||
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) |
||||||
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) |
||||||
|
|
||||||
|
while (True): |
||||||
|
ret, img = cap.read() |
||||||
|
if ret: |
||||||
|
img = cv2.warpPerspective(img, trans_webcam_to_eon_front, (1152,864), borderMode=cv2.BORDER_CONSTANT, borderValue=0) |
||||||
|
img = img[:,-864//2:,:] |
||||||
|
cv2.imshow('preview', img) |
||||||
|
cv2.waitKey(10) |
@ -0,0 +1,45 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
import numpy as np |
||||||
|
|
||||||
|
# copied from common.transformations/camera.py |
||||||
|
eon_focal_length = 910.0 # pixels |
||||||
|
eon_dcam_focal_length = 860.0 # pixels |
||||||
|
|
||||||
|
webcam_focal_length = 908.0 # pixels |
||||||
|
|
||||||
|
eon_intrinsics = np.array([ |
||||||
|
[eon_focal_length, 0., 1164/2.], |
||||||
|
[ 0., eon_focal_length, 874/2.], |
||||||
|
[ 0., 0., 1.]]) |
||||||
|
|
||||||
|
eon_dcam_intrinsics = np.array([ |
||||||
|
[eon_dcam_focal_length, 0, 1152/2.], |
||||||
|
[ 0, eon_dcam_focal_length, 864/2.], |
||||||
|
[ 0, 0, 1]]) |
||||||
|
|
||||||
|
webcam_intrinsics = np.array([ |
||||||
|
[webcam_focal_length, 0., 1280/2.], |
||||||
|
[ 0., webcam_focal_length, 720/2.], |
||||||
|
[ 0., 0., 1.]]) |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
import cv2 |
||||||
|
trans_webcam_to_eon_rear = np.dot(eon_intrinsics,np.linalg.inv(webcam_intrinsics)) |
||||||
|
trans_webcam_to_eon_front = np.dot(eon_dcam_intrinsics,np.linalg.inv(webcam_intrinsics)) |
||||||
|
print("trans_webcam_to_eon_rear:\n", trans_webcam_to_eon_rear) |
||||||
|
print("trans_webcam_to_eon_front:\n", trans_webcam_to_eon_front) |
||||||
|
|
||||||
|
cap = cv2.VideoCapture(1) |
||||||
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) |
||||||
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) |
||||||
|
|
||||||
|
while (True): |
||||||
|
ret, img = cap.read() |
||||||
|
if ret: |
||||||
|
# img = cv2.warpPerspective(img, trans_webcam_to_eon_rear, (1164,874), borderMode=cv2.BORDER_CONSTANT, borderValue=0) |
||||||
|
img = cv2.warpPerspective(img, trans_webcam_to_eon_front, (1164,874), borderMode=cv2.BORDER_CONSTANT, borderValue=0) |
||||||
|
print(img.shape, end='\r') |
||||||
|
cv2.imshow('preview', img) |
||||||
|
cv2.waitKey(10) |
||||||
|
|
||||||
|
|
Loading…
Reference in new issue