Make the camera work in bayer mode

This commit is contained in:
Martijn Braam 2020-09-02 19:45:28 +02:00
parent 38ec7621fc
commit 38fd591828
6 changed files with 2663 additions and 39 deletions

View File

@ -15,5 +15,5 @@ LINK_DIRECTORIES(${GTK3_LIBRARY_DIRS})
# Add other flags to the compiler
ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER})
add_executable(GTKCamera main.c ini.c ini.h)
add_executable(GTKCamera main.c ini.c ini.h bayer.c bayer.h)
target_link_libraries(GTKCamera ${GTK3_LIBRARIES})

2187
bayer.c Normal file

File diff suppressed because it is too large Load Diff

87
bayer.h Normal file
View File

@ -0,0 +1,87 @@
#include <stdint.h>
typedef enum {
DC1394_BAYER_METHOD_NEAREST=0,
DC1394_BAYER_METHOD_SIMPLE,
DC1394_BAYER_METHOD_BILINEAR,
DC1394_BAYER_METHOD_HQLINEAR,
DC1394_BAYER_METHOD_DOWNSAMPLE,
DC1394_BAYER_METHOD_EDGESENSE,
DC1394_BAYER_METHOD_VNG,
DC1394_BAYER_METHOD_AHD
} dc1394bayer_method_t;
typedef enum {
DC1394_COLOR_FILTER_RGGB = 512,
DC1394_COLOR_FILTER_GBRG,
DC1394_COLOR_FILTER_GRBG,
DC1394_COLOR_FILTER_BGGR
} dc1394color_filter_t ;
#define DC1394_COLOR_FILTER_MIN DC1394_COLOR_FILTER_RGGB
#define DC1394_COLOR_FILTER_MAX DC1394_COLOR_FILTER_BGGR
#define DC1394_COLOR_FILTER_NUM (DC1394_COLOR_FILTER_MAX - DC1394_COLOR_FILTER_MIN + 1)
/**
* Error codes returned by most libdc1394 functions.
*
* General rule: 0 is success, negative denotes a problem.
*/
typedef enum {
DC1394_SUCCESS = 0,
DC1394_FAILURE = -1,
DC1394_NOT_A_CAMERA = -2,
DC1394_FUNCTION_NOT_SUPPORTED = -3,
DC1394_CAMERA_NOT_INITIALIZED = -4,
DC1394_MEMORY_ALLOCATION_FAILURE = -5,
DC1394_TAGGED_REGISTER_NOT_FOUND = -6,
DC1394_NO_ISO_CHANNEL = -7,
DC1394_NO_BANDWIDTH = -8,
DC1394_IOCTL_FAILURE = -9,
DC1394_CAPTURE_IS_NOT_SET = -10,
DC1394_CAPTURE_IS_RUNNING = -11,
DC1394_RAW1394_FAILURE = -12,
DC1394_FORMAT7_ERROR_FLAG_1 = -13,
DC1394_FORMAT7_ERROR_FLAG_2 = -14,
DC1394_INVALID_ARGUMENT_VALUE = -15,
DC1394_REQ_VALUE_OUTSIDE_RANGE = -16,
DC1394_INVALID_FEATURE = -17,
DC1394_INVALID_VIDEO_FORMAT = -18,
DC1394_INVALID_VIDEO_MODE = -19,
DC1394_INVALID_FRAMERATE = -20,
DC1394_INVALID_TRIGGER_MODE = -21,
DC1394_INVALID_TRIGGER_SOURCE = -22,
DC1394_INVALID_ISO_SPEED = -23,
DC1394_INVALID_IIDC_VERSION = -24,
DC1394_INVALID_COLOR_CODING = -25,
DC1394_INVALID_COLOR_FILTER = -26,
DC1394_INVALID_CAPTURE_POLICY = -27,
DC1394_INVALID_ERROR_CODE = -28,
DC1394_INVALID_BAYER_METHOD = -29,
DC1394_INVALID_VIDEO1394_DEVICE = -30,
DC1394_INVALID_OPERATION_MODE = -31,
DC1394_INVALID_TRIGGER_POLARITY = -32,
DC1394_INVALID_FEATURE_MODE = -33,
DC1394_INVALID_LOG_TYPE = -34,
DC1394_INVALID_BYTE_ORDER = -35,
DC1394_INVALID_STEREO_METHOD = -36,
DC1394_BASLER_NO_MORE_SFF_CHUNKS = -37,
DC1394_BASLER_CORRUPTED_SFF_CHUNK = -38,
DC1394_BASLER_UNKNOWN_SFF_CHUNK = -39
} dc1394error_t;
#define DC1394_ERROR_MIN DC1394_BASLER_UNKNOWN_SFF_CHUNK
#define DC1394_ERROR_MAX DC1394_SUCCESS
#define DC1394_ERROR_NUM (DC1394_ERROR_MAX-DC1394_ERROR_MIN+1)
typedef enum {
DC1394_FALSE= 0,
DC1394_TRUE
} dc1394bool_t;
dc1394error_t
dc1394_bayer_decoding_8bit(const uint8_t * bayer, uint8_t * rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method);
dc1394error_t
dc1394_bayer_decoding_16bit(const uint16_t * bayer, uint16_t * rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method, uint32_t bits);

BIN
build/GTKCamera Executable file

Binary file not shown.

408
main.c
View File

@ -1,11 +1,18 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <linux/media.h>
#include <linux/v4l2-subdev.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <linux/kdev_t.h>
#include <sys/sysmacros.h>
#include <asm/errno.h>
#include <gtk/gtk.h>
#include "ini.h"
#include "bayer.h"
enum io_method {
IO_METHOD_READ,
@ -21,14 +28,40 @@ struct buffer {
struct buffer *buffers;
static int *outbuffer;
static unsigned int n_buffers;
static char *rear_dev_name;
static char *front_dev_name;
static char *dev_name;
static enum io_method io = IO_METHOD_MMAP;
static int preview_width = -1;
static int preview_height = -1;
static int preview_fmt = V4L2_PIX_FMT_RGB24;
// Rear camera
static char *rear_dev_name;
static int *rear_entity_id;
static char *rear_dev[20];
static int rear_width = -1;
static int rear_height = -1;
static int rear_rotate = 0;
static int rear_fmt = V4L2_PIX_FMT_RGB24;
static int rear_mbus = MEDIA_BUS_FMT_RGB888_1X24;
// Front camera
static char *front_dev_name;
static int *front_entity_id;
static char *front_dev[20];
static int front_width = -1;
static int front_height = -1;
static int front_rotate = 0;
static int front_fmt = V4L2_PIX_FMT_RGB24;
static int front_mbus = MEDIA_BUS_FMT_RGB888_1X24;
// Camera interface
static char *media_drv_name;
static int *interface_entity_id;
static char *dev_name[20];
static int media_fd;
// State
static int current_width = -1;
static int current_height = -1;
static int current_fmt = 0;
static int current_rotate = 0;
static int capture = 0;
GObject *preview_image;
@ -153,6 +186,32 @@ init_mmap(int fd)
}
}
static void
init_sensor(char* fn, int width, int height, int mbus)
{
int fd;
struct v4l2_subdev_format fmt;
fd = open(fn, O_RDWR);
g_printerr("Setting sensor to %dx%d fmt %d\n",
width, height, mbus);
fmt.pad = 0;
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
fmt.format.code = mbus;
fmt.format.width = width;
fmt.format.height = height;
fmt.format.field = V4L2_FIELD_ANY;
if (xioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) {
errno_exit("VIDIOC_SUBDEV_S_FMT");
}
g_printerr("Driver returned %dx%d fmt %d\n",
fmt.format.width, fmt.format.height,
fmt.format.code);
close(fd);
}
static void
init_device(int fd)
{
@ -219,12 +278,12 @@ init_device(int fd)
struct v4l2_format fmt = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
if (preview_width > 0) {
if (current_width > 0) {
g_printerr("Setting camera to %dx%d fmt %d\n",
preview_width, preview_height, preview_fmt);
fmt.fmt.pix.width = preview_width;
fmt.fmt.pix.height = preview_height;
fmt.fmt.pix.pixelformat = preview_fmt;
current_width, current_height, current_fmt);
fmt.fmt.pix.width = current_width;
fmt.fmt.pix.height = current_height;
fmt.fmt.pix.pixelformat = current_fmt;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
@ -246,10 +305,10 @@ init_device(int fd)
g_printerr("Driver returned %dx%d fmt %d\n",
fmt.fmt.pix.width, fmt.fmt.pix.height,
fmt.fmt.pix.pixelformat);
preview_width = fmt.fmt.pix.width;
preview_height = fmt.fmt.pix.height;
current_width = fmt.fmt.pix.width;
current_height = fmt.fmt.pix.height;
}
preview_fmt = fmt.fmt.pix.pixelformat;
current_fmt = fmt.fmt.pix.pixelformat;
/* Buggy driver paranoia. */
unsigned int min = fmt.fmt.pix.width * 2;
@ -279,11 +338,54 @@ init_device(int fd)
static void
process_image(const int *p, int size)
{
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(p, GDK_COLORSPACE_RGB,
FALSE, 8, 640, 480, 2 * 640,
NULL, NULL);
gtk_image_set_from_pixbuf(preview_image, pixbuf
);
clock_t t;
time_t rawtime;
uint8_t *pixels;
double time_taken;
char fname[255];
GdkPixbuf *pixbuf;
GdkPixbuf *pixbufrot;
GError *error = NULL;
t = clock();
dc1394bayer_method_t method = DC1394_BAYER_METHOD_DOWNSAMPLE;
dc1394color_filter_t filter = DC1394_COLOR_FILTER_BGGR;
if(capture){
method = DC1394_BAYER_METHOD_SIMPLE;
// method = DC1394_BAYER_METHOD_VNG is slightly sharper but takes 10 seconds;
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, current_width, current_height);
}else{
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, current_width/2, current_height/2);
}
pixels = gdk_pixbuf_get_pixels(pixbuf);
dc1394_bayer_decoding_8bit((const uint8_t*)p, pixels, current_width, current_height, filter, method);
if (current_rotate == 0) {
pixbufrot = pixbuf;
} else if (current_rotate == 90) {
pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE);
} else if (current_rotate == 180) {
pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_UPSIDEDOWN);
} else if (current_rotate == 270) {
pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE);
}
if (capture){
time(&rawtime);
sprintf(fname, "%s/Pictures/Photo-%s.jpg", getenv("HOME"), ctime(&rawtime));
printf("Saving image\n");
gdk_pixbuf_save(pixbufrot, fname, "jpeg", &error, "quality", "85", NULL);
if(error != NULL) {
g_printerr(error->message);
g_clear_error(&error);
}
} else {
gtk_image_set_from_pixbuf(preview_image, pixbufrot);
}
capture = 0;
t = clock() - t;
time_taken = ((double)t)/CLOCKS_PER_SEC;
printf("%f fps\n", 1.0/time_taken);
}
static int
@ -405,42 +507,97 @@ static int
config_ini_handler(void *user, const char *section, const char *name,
const char *value)
{
if (strcmp(section, "preview") == 0) {
if (strcmp(section, "rear") == 0) {
if (strcmp(name, "width") == 0) {
preview_width = strtol(value, NULL, 10);
rear_width = strtol(value, NULL, 10);
} else if (strcmp(name, "height") == 0) {
preview_height = strtol(value, NULL, 10);
rear_height = strtol(value, NULL, 10);
} else if (strcmp(name, "rotate") == 0) {
rear_rotate = strtol(value, NULL, 10);
} else if (strcmp(name, "fmt") == 0) {
if (strcmp(value, "RGB") == 0) {
preview_fmt = V4L2_PIX_FMT_RGB24;
rear_fmt = V4L2_PIX_FMT_RGB24;
} else if (strcmp(value, "UYVY") == 0) {
preview_fmt = V4L2_PIX_FMT_UYVY;
rear_fmt = V4L2_PIX_FMT_UYVY;
} else if (strcmp(value, "YUYV") == 0) {
preview_fmt = V4L2_PIX_FMT_YUYV;
rear_fmt = V4L2_PIX_FMT_YUYV;
} else if (strcmp(value, "JPEG") == 0) {
preview_fmt = V4L2_PIX_FMT_JPEG;
rear_fmt = V4L2_PIX_FMT_JPEG;
} else if (strcmp(value, "NV12") == 0) {
preview_fmt = V4L2_PIX_FMT_NV12;
rear_fmt = V4L2_PIX_FMT_NV12;
} else if (strcmp(value, "YUV420") == 0
|| strcmp(value, "I420") == 0
|| strcmp(value, "YU12") == 0) {
preview_fmt = V4L2_PIX_FMT_YUV420;
rear_fmt = V4L2_PIX_FMT_YUV420;
} else if (strcmp(value, "YVU420") == 0
|| strcmp(value, "YV12") == 0) {
preview_fmt = V4L2_PIX_FMT_YVU420;
rear_fmt = V4L2_PIX_FMT_YVU420;
} else if (strcmp(value, "RGGB8") == 0) {
rear_fmt = V4L2_PIX_FMT_SRGGB8;
} else if (strcmp(value, "BGGR8") == 0) {
rear_fmt = V4L2_PIX_FMT_SBGGR8;
rear_mbus = MEDIA_BUS_FMT_SBGGR8_1X8;
} else if (strcmp(value, "GRBG8") == 0) {
rear_fmt = V4L2_PIX_FMT_SGRBG8;
} else if (strcmp(value, "GBRG8") == 0) {
rear_fmt = V4L2_PIX_FMT_SGBRG8;
} else {
g_printerr("Unsupported pixelformat %s\n", value);
exit(1);
}
} else if (strcmp(name, "driver") == 0){
rear_dev_name = strdup(value);
} else {
g_printerr("Unknown key '%s' in [preview]\n", name);
g_printerr("Unknown key '%s' in [rear]\n", name);
exit(1);
}
} else if (strcmp(section, "front") == 0) {
if (strcmp(name, "width") == 0) {
front_width = strtol(value, NULL, 10);
} else if (strcmp(name, "height") == 0) {
front_height = strtol(value, NULL, 10);
} else if (strcmp(name, "rotate") == 0) {
front_rotate = strtol(value, NULL, 10);
} else if (strcmp(name, "fmt") == 0) {
if (strcmp(value, "RGB") == 0) {
front_fmt = V4L2_PIX_FMT_RGB24;
} else if (strcmp(value, "UYVY") == 0) {
front_fmt = V4L2_PIX_FMT_UYVY;
} else if (strcmp(value, "YUYV") == 0) {
front_fmt = V4L2_PIX_FMT_YUYV;
} else if (strcmp(value, "JPEG") == 0) {
front_fmt = V4L2_PIX_FMT_JPEG;
} else if (strcmp(value, "NV12") == 0) {
front_fmt = V4L2_PIX_FMT_NV12;
} else if (strcmp(value, "YUV420") == 0
|| strcmp(value, "I420") == 0
|| strcmp(value, "YU12") == 0) {
front_fmt = V4L2_PIX_FMT_YUV420;
} else if (strcmp(value, "YVU420") == 0
|| strcmp(value, "YV12") == 0) {
front_fmt = V4L2_PIX_FMT_YVU420;
} else if (strcmp(value, "RGGB8") == 0) {
front_fmt = V4L2_PIX_FMT_SRGGB8;
} else if (strcmp(value, "BGGR8") == 0) {
front_fmt = V4L2_PIX_FMT_SBGGR8;
front_mbus = MEDIA_BUS_FMT_SBGGR8_1X8;
} else if (strcmp(value, "GRBG8") == 0) {
front_fmt = V4L2_PIX_FMT_SGRBG8;
} else if (strcmp(value, "GBRG8") == 0) {
front_fmt = V4L2_PIX_FMT_SGBRG8;
} else {
g_printerr("Unsupported pixelformat %s\n", value);
exit(1);
}
} else if (strcmp(name, "driver") == 0){
front_dev_name = strdup(value);
} else {
g_printerr("Unknown key '%s' in [front]\n", name);
exit(1);
}
} else if (strcmp(section, "device") == 0) {
if (strcmp(name, "rear") == 0) {
rear_dev_name = strdup(value);
} else if (strcmp(name, "front") == 0) {
front_dev_name = strdup(value);
if (strcmp(name, "csi") == 0) {
media_drv_name = strdup(value);
} else {
g_printerr("Unknown key '%s' in [device]\n", name);
exit(1);
@ -452,6 +609,173 @@ config_ini_handler(void *user, const char *section, const char *name,
return 1;
}
int
find_dev_node(int maj, int min, char* fnbuf)
{
DIR *d;
struct dirent *dir;
struct stat info;
d = opendir("/dev");
while ((dir = readdir(d)) != NULL) {
sprintf(fnbuf, "/dev/%s", dir->d_name);
stat(fnbuf, &info);
if (!S_ISCHR(info.st_mode)){
continue;
}
if (major(info.st_rdev) == maj && minor(info.st_rdev) == min) {
return 0;
}
}
return -1;
}
int
setup_rear()
{
struct media_link_desc link = {0};
// Disable the interface<->front link
link.flags = 0;
link.source.entity = front_entity_id;
link.source.index = 0;
link.sink.entity = interface_entity_id;
link.sink.index = 0;
if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){
g_printerr("Could not disable front camera link\n");
return -1;
}
// Enable the interface<->rear link
link.flags = MEDIA_LNK_FL_ENABLED;
link.source.entity = rear_entity_id;
link.source.index = 0;
link.sink.entity = interface_entity_id;
link.sink.index = 0;
if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){
g_printerr("Could not enable rear camera link\n");
return -1;
}
current_width = rear_width;
current_height = rear_height;
current_fmt = rear_fmt;
current_rotate = rear_rotate;
// Find camera node
init_sensor(rear_dev, rear_width, rear_height, rear_mbus);
return 0;
}
int
setup_front()
{
struct media_link_desc link = {0};
// Disable the interface<->rear link
link.flags = 0;
link.source.entity = rear_entity_id;
link.source.index = 0;
link.sink.entity = interface_entity_id;
link.sink.index = 0;
if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){
g_printerr("Could not disable front camera link\n");
return -1;
}
// Enable the interface<->rear link
link.flags = MEDIA_LNK_FL_ENABLED;
link.source.entity = front_entity_id;
link.source.index = 0;
link.sink.entity = interface_entity_id;
link.sink.index = 0;
if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){
g_printerr("Could not enable rear camera link\n");
return -1;
}
current_width = front_width;
current_height = front_height;
current_fmt = front_fmt;
current_rotate = front_rotate;
// Find camera node
init_sensor(front_dev, front_width, front_height, front_mbus);
return 0;
}
int
find_cameras()
{
struct media_entity_desc entity = {0};
int ret;
int found = 0;
while (1) {
entity.id = entity.id | MEDIA_ENT_ID_FLAG_NEXT;
ret = xioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
if (ret < 0){
break;
}
printf("At node %s, (0x%x)\n", entity.name, entity.type);
if(strncmp(entity.name, front_dev_name, strlen(front_dev_name)) == 0) {
front_entity_id = entity.id;
find_dev_node(entity.dev.major, entity.dev.minor, front_dev);
printf("Found front cam, is %s at %s\n", entity.name, front_dev);
found++;
}
if(strncmp(entity.name, rear_dev_name, strlen(rear_dev_name)) == 0) {
rear_entity_id = entity.id;
find_dev_node(entity.dev.major, entity.dev.minor, rear_dev);
printf("Found rear cam, is %s at %s\n", entity.name, rear_dev);
found++;
}
if (entity.type == MEDIA_ENT_F_IO_V4L) {
interface_entity_id = entity.id;
find_dev_node(entity.dev.major, entity.dev.minor, dev_name);
printf("Found v4l2 interface node at %s\n", dev_name);
}
}
if(found < 2){
return -1;
}
return 0;
}
int
find_media_fd()
{
DIR *d;
struct dirent *dir;
int fd;
char fnbuf[20];
struct media_device_info mdi = {0};
d = opendir("/dev");
while ((dir = readdir(d)) != NULL) {
if(strncmp(dir->d_name, "media", 5) == 0) {
sprintf(fnbuf, "/dev/%s", dir->d_name);
printf("Checking %s\n", fnbuf);
fd = open(fnbuf, O_RDWR);
xioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi);
printf("Found media device: %s\n", mdi.driver);
if (strcmp(mdi.driver, media_drv_name) == 0){
media_fd = fd;
return 0;
}
close(fd);
}
}
return 1;
}
void
on_shutter_clicked(GtkWidget *widget, gpointer user_data)
{
capture = 1;
}
int
main(int argc, char *argv[])
{
@ -475,8 +799,10 @@ main(int argc, char *argv[])
GObject *window = gtk_builder_get_object(builder, "window");
GObject *preview_box = gtk_builder_get_object(builder, "preview_box");
GObject *shutter = gtk_builder_get_object(builder, "shutter");
preview_image = gtk_builder_get_object(builder, "preview");
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
GtkCssProvider *provider = gtk_css_provider_new();
if (access("camera.css", F_OK) != -1) {
@ -501,7 +827,15 @@ main(int argc, char *argv[])
return 1;
}
dev_name = rear_dev_name;
if (find_media_fd() == -1) {
g_printerr("Could not find the media node\n");
return 1;
}
if (find_cameras() == -1) {
g_printerr("Could not find the cameras\n");
return 1;
}
setup_rear();
int fd = open(dev_name, O_RDWR);
if (fd == -1) {
@ -513,9 +847,9 @@ main(int argc, char *argv[])
start_capturing(fd);
// Get a new frame every 34ms ~30fps
g_timeout_add(34, get_frame, fd);
printf("window show\n");
gtk_widget_show(window);
g_idle_add(get_frame, fd);
gtk_main();
return 0;
}

16
pinephone.ini Normal file
View File

@ -0,0 +1,16 @@
[device]
csi=sun6i-csi
[rear]
driver=ov5640
width=1280
height=720
fmt=BGGR8
rotate=270
[front]
driver=gc2145
width=1280
height=720
fmt=BGGR8
rotate=90