diff --git a/components/vision/include/base/maix_camera_base.hpp b/components/vision/include/base/maix_camera_base.hpp deleted file mode 100644 index ab83ffc4..00000000 --- a/components/vision/include/base/maix_camera_base.hpp +++ /dev/null @@ -1,163 +0,0 @@ -/** - * @file maix_camera_base.hpp - * @brief Maix camera SDL implementation - * @author neucrack@sipeed.com - * @license Apache 2.0 Sipeed Ltd - * @update date 2023-10-23 Create by neucrack -*/ - -#pragma once - -#include -#include "maix_image.hpp" -#include "maix_err.hpp" - -namespace maix::camera -{ - class CameraBase - { - public: - /** - * @brief Construct a new Camera object - * @param device camera device name, you can get devices by list_devices method, by default(value is NULL(None in MaixPy)) means the first device - * @param width camera width, by default(value is -1) means auto detect, - * if width > max device supported width, will auto set to max device supported width - * @param height camera height, by default(value is -1) means auto detect, - * if height > max device supported height, will auto set to max device supported height - * @param format camera format, by default(value is FMT_RGB888) - * @param buff_num camera buffer number, by default(value is 3) - */ - CameraBase(const char *device = nullptr, int width = -1, int height = -1, image::Format format = image::Format::FMT_RGB888, int buff_num = 3){}; - - /** - * @brief Judge if the given format is supported by the camera - */ - virtual bool is_support_format(image::Format format) = 0; - - /** - * @brief open camera device - * @param width camera width, by default(value is -1) means auto detect, - * if width > max device supported width, will auto set to max device supported width - * @param height camera height, by default(value is -1) means auto detect, - * if height > max device supported height, will auto set to max device supported height - * @param format camera format, by default(value is FMT_RGB888) - * @param buff_num camera buffer number, by default(value is 3) - * @return error code - */ - virtual err::Err open(int width = -1, int height = -1, image::Format format = image::Format::FMT_RGB888, int buff_num = 3) = 0; - - /** - * @brief read a frame from camera - * @param buff buffer to store image data, if NULL, will alloc a new buffer - * @return image data - */ - virtual image::Image *read(void *buff = NULL, size_t buff_size = 0) = 0; - - /** - * @brief close camera device - * @return none - */ - virtual void close() = 0; - - /** - * Add a new channel and return a new Camera object, you can use close() to close this channel. - * @param width camera width, default is -1, means auto, mostly means max width of camera support - * @param height camera height, default is -1, means auto, mostly means max height of camera support - * @param format camera output format, default is RGB888 - * @param buff_num camera buffer number, default is 3, means 3 buffer, one used by user, one used for cache the next frame, - * more than one buffer will accelerate image read speed, but will cost more memory. - * @return new Camera object - */ - virtual camera::CameraBase *add_channel(int width = -1, int height = -1, image::Format format = image::FMT_RGB888, int buff_num = 3) = 0; - - /** - * @brief clear all buffer - * @return none - */ - virtual void clear_buff() = 0; - - /** - * @brief check camera device is opened or not - * @return opened or not, bool type - */ - virtual bool is_opened() = 0; - - /** - * Get camera supported channels(layers) - */ - virtual int get_ch_nums() = 0; - - /** - * Get channel number of camera. - */ - virtual int get_channel() = 0; - - /** - * Set/Get camera mirror - * @param en enable/disable mirror - */ - virtual int hmirror(int en) = 0; - - /** - * Set/Get camera flip - * @param en enable/disable flip - */ - virtual int vflip(int en) = 0; - - /** - * Set/Get camera luma - * @param int luma value - */ - virtual int luma(int value) = 0; - - /** - * Set/Get camera constrast - * @param int constrast value - */ - virtual int constrast(int value) = 0; - - /** - * Set/Get camera saturation - * @param int saturation value - */ - virtual int saturation(int value) = 0; - - /** - * Set/Get camera exposure - * @param int constrast value - */ - virtual int exposure(int value) = 0; - - /** - * Set/Get camera gain - * @param int saturation value - */ - virtual int gain(int gain) = 0; - - /** - * Set/Get awb mode - * @attention This method will affect the isp and thus the image, so please be careful with it. - * @param value value = 0, means set awb to auto mode, value = 1, means set awb to manual mode, default is auto mode. - * @return returns awb mode - */ - virtual int awb_mode(int value = -1) = 0; - - /** - * Set/Get exp mode - * @attention This method will affect the isp and thus the image, so please be careful with it. - * @param value value = 0, means set exposure to auto mode, value = 1, means set exposure to manual mode, default is auto mode. - * @return returns exposure mode - */ - virtual int exp_mode(int value = -1) = 0; - - /** - * Set window size of camera - * @param roi Support two input formats, [x,y,w,h] set the coordinates and size of the window; - * [w,h] set the size of the window, when the window is centred. - * @return error code - */ - virtual int set_windowing(std::vector roi) = 0; - }; - -} - diff --git a/components/vision/include/maix_camera.hpp b/components/vision/include/maix_camera.hpp index c823352b..4d2fec76 100644 --- a/components/vision/include/maix_camera.hpp +++ b/components/vision/include/maix_camera.hpp @@ -11,7 +11,6 @@ #include "maix_log.hpp" #include "maix_image.hpp" #include "maix_err.hpp" -#include "maix_camera_base.hpp" #include #include #include @@ -60,23 +59,6 @@ namespace maix::camera * @maixcdk maix.camera.Camera.Camera */ Camera(int width = -1, int height = -1, image::Format format = image::FMT_RGB888, const char *device = nullptr, int fps = -1, int buff_num = 3, bool open = true); - - /** - * @brief Construct a new Camera object. - * @attention CameraBase * parameter need to be set manually, otherwise the operation of this object will be invalid. - * @param device camera device path, you can get devices by list_devices method, by default(value is NULL(None in MaixPy)) means the first device - * @param base basic operation objects. - * @param width camera width, default is -1, means auto, mostly means max width of camera support - * @param height camera height, default is -1, means auto, mostly means max height of camera support - * @param format camera output format, default is image.Format.FMT_RGB888 - * @param fps camera fps, default is -1, means auto, mostly means max fps of camera support - * @param buff_num camera buffer number, default is 3, means 3 buffer, one used by user, one used for cache the next frame, - * more than one buffer will accelerate image read speed, but will cost more memory. - * @param open If true, camera will automatically call open() after creation. default is true. - * @maixcdk maix.camera.Camera.Camera - */ - Camera(const char *device, CameraBase *base, int width = -1, int height = -1, image::Format format = image::FMT_RGB888, int fps = -1, int buff_num = -1, bool open = true); - ~Camera(); /** @@ -389,8 +371,8 @@ namespace maix::camera float _gain; bool _show_colorbar; bool _open_set_regs; - CameraBase *_impl; // used by implement code bool _check_format(image::Format format); uint64_t _last_read_us; + bool _is_opened; }; } diff --git a/components/vision/port/linux/maix_camera_v4l2.hpp b/components/vision/port/linux/maix_camera_v4l2.cpp similarity index 81% rename from components/vision/port/linux/maix_camera_v4l2.hpp rename to components/vision/port/linux/maix_camera_v4l2.cpp index 4d3b04bb..9ec129ce 100644 --- a/components/vision/port/linux/maix_camera_v4l2.hpp +++ b/components/vision/port/linux/maix_camera_v4l2.cpp @@ -1,12 +1,15 @@ /** - * @author neucrack@sipeed + * @author neucrack@sipeed, lxowalle@sipeed * @copyright Sipeed Ltd 2023- * @license Apache 2.0 * @update 2023.9.8: Add framework, create this file. */ -#pragma once +#include "maix_camera.hpp" +#include "maix_basic.hpp" +#include + #include #include @@ -26,7 +29,6 @@ #include "maix_err.hpp" #include "maix_log.hpp" #include "maix_image.hpp" -#include "maix_camera_base.hpp" #ifndef V4L2_PIX_FMT_RGBA32 #define V4L2_PIX_FMT_RGBA32 v4l2_fourcc('R', 'G', 'B', 'A') /* 32 RGBA-8-8-8-8 */ @@ -491,7 +493,9 @@ namespace maix::camera return ret; } - class CameraV4L2 final : public CameraBase + static bool set_regs_flag = false; + + class CameraV4L2 { public: CameraV4L2(const std::string device, int width, int height, image::Format format, int buff_num) @@ -1060,4 +1064,328 @@ namespace maix::camera bool buff_alloc; }; -} // namespace maix::camera + std::vector list_devices() + { + // find to /dev/video* + std::vector devices; + std::string path = "/dev"; + DIR *dir = opendir(path.c_str()); + if (dir == NULL) + { + return devices; + } + struct dirent *ptr; + while ((ptr = readdir(dir)) != NULL) + { + if (ptr->d_type == DT_CHR) + { + std::string name = ptr->d_name; + if (name.find("video") != std::string::npos) + { + devices.push_back( path + "/" + name ); + } + } + } + closedir(dir); + // sort devices with name + std::sort(devices.begin(), devices.end()); + // print devices + for (size_t i = 0; i < devices.size(); i++) + { + log::debug("find device: %s\n", devices[i].c_str()); + } + return devices; + } + + void set_regs_enable(bool enable) { + set_regs_flag = enable; + } + + err::Err Camera::show_colorbar(bool enable) + { + // only set variable now + // should control camera to show colorbar + _show_colorbar = enable; + return err::ERR_NONE; + } + + static void generate_colorbar(image::Image &img) + { + int width = img.width(); + int height = img.height(); + int step = width / 8; + int color_step = 255 / 7; + int color = 0; + uint8_t colors[8][3] = { + {255, 255, 255}, + {255, 0, 0}, + {255, 127, 0}, + {255, 255, 0}, + {0, 255, 0}, + {0, 0, 255}, + {143, 0, 255}, + {0, 0, 0}, + }; + for (int i = 0; i < 8; i++) + { + image::Color _color(colors[i][0], colors[i][1], colors[i][2], 0, image::FMT_RGB888); + img.draw_rect(i * step, 0, step, height, _color, -1); + color += color_step; + } + } + + static char * _get_device(const char *device) + { + if (device) + { + return (char *)device; + } + else + { + std::vector devices = list_devices(); + err::check_bool_raise(devices.size() > 0, "No camera device"); + return (char *)devices[0].c_str(); + } + } + + static CameraV4L2 *_impl; + Camera::Camera(int width, int height, image::Format format, const char *device, int fps, int buff_num, bool open) + { + err::Err e; + err::check_bool_raise(_check_format(format), "Format not support"); + + if (format == image::Format::FMT_RGB888 && width * height * 3 > 640 * 640 * 3) { + log::warn("Note that we do not recommend using large resolution RGB888 images, which can take up a lot of memory!\r\n"); + } + + _width = (width == -1) ? 640 : width; + _height = (height == -1) ? 480 : height; + _format = format; + _buff_num = buff_num; + _show_colorbar = false; + _open_set_regs = set_regs_flag; + + _fps = (fps == -1) ? 30 : fps; + if (device ) { + _device = _get_device(strlen(device) == 0 ? NULL : device); + } else { + _device = _get_device(NULL); + } + _impl = new CameraV4L2(_device, _width, _height, _format, _buff_num); + + + if (open) { + e = this->open(_width, _height, _format, _fps, _buff_num); + err::check_raise(e, "camera open failed"); + } + } + + Camera::~Camera() + { + if (this->is_opened()) { + this->close(); + } + delete (CameraV4L2*)_impl; + } + + int Camera::get_ch_nums() + { + return 1; + } + + int Camera::get_channel() + { + if (_impl == NULL) + return err::ERR_NOT_INIT; + + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + return _impl->get_channel(); + } + + bool Camera::_check_format(image::Format format) { + if (format == image::FMT_RGB888 || format == image::FMT_BGR888 + || format == image::FMT_RGBA8888 || format == image::FMT_BGRA8888 + || format == image::FMT_YVU420SP || format == image::FMT_GRAYSCALE) { + return true; + } else { + return false; + } + } + + err::Err Camera::open(int width, int height, image::Format format, int fps, int buff_num) + { + if (_impl == NULL) + return err::Err::ERR_RUNTIME; + + int width_tmp = (width == -1) ? _width : width; + int height_tmp = (height == -1) ? _height : height; + image::Format format_tmp = (format == image::FMT_INVALID) ? _format : format; + int fps_tmp = (fps == -1) ? 30 : fps; + int buff_num_tmp =( buff_num == -1) ? _buff_num : buff_num; + + err::check_bool_raise(_check_format(format_tmp), "Format not support"); + + if (this->is_opened()) { + if (width == width_tmp && height == height_tmp && format == format_tmp && fps == fps_tmp && buff_num == buff_num_tmp) { + return err::ERR_NONE; + } + this->close(); // Get new param, close and reopen + } + + _width = width_tmp; + _height = height_tmp; + _fps = fps_tmp; + _buff_num = buff_num_tmp; + _format = format_tmp; + _format_impl = _format; + if(!_impl->is_support_format(_format)) + { + if(_impl->is_support_format(image::FMT_RGB888)) + _format_impl = image::FMT_RGB888; + else if(_impl->is_support_format(image::FMT_BGR888)) + _format_impl = image::FMT_BGR888; + else if(_impl->is_support_format(image::FMT_YVU420SP)) + _format_impl = image::FMT_YVU420SP; + else if(_impl->is_support_format(image::FMT_YUV420SP)) + _format_impl = image::FMT_YUV420SP; + else if(_impl->is_support_format(image::FMT_RGBA8888)) + _format_impl = image::FMT_RGBA8888; + else if(_impl->is_support_format(image::FMT_BGRA8888)) + _format_impl = image::FMT_BGRA8888; + else if(_impl->is_support_format(image::FMT_GRAYSCALE)) + _format_impl = image::FMT_GRAYSCALE; + else + return err::ERR_ARGS; + } + + return _impl->open(_width, _height, _format_impl, _buff_num);; + } + + void Camera::close() + { + if (this->is_closed()) + return; + } + + camera::Camera *Camera::add_channel(int width, int height, image::Format format, int fps, int buff_num, bool open) + { + return NULL; + } + + bool Camera::is_opened() + { + return _is_opened; + } + + image::Image *Camera::read(void *buff, size_t buff_size, bool block) + { + if (!this->is_opened()) { + err::Err e = open(_width, _height, _format, _buff_num); + err::check_raise(e, "open camera failed"); + } + + if (_show_colorbar) { + image::Image *img = new image::Image(_width, _height); + generate_colorbar(*img); + err::check_null_raise(img, "camera read failed"); + return img; + } else { + // it's better all done by impl to faster read, but if impl not support, we have to convert it + if(_format_impl == _format) + { + image::Image *img = _impl->read(buff, buff_size); + err::check_null_raise(img, "camera read failed"); + + // FIXME: delete me and fix driver bug + uint64_t wait_us = 1000000 / _fps; + while (time::ticks_us() - _last_read_us < wait_us) { + time::sleep_us(50); + } + _last_read_us = time::ticks_us(); + return img; + } + else + { + image::Image *img = _impl->read(); + image::Image *img2 = img->to_format(_format, buff, buff_size); + delete img; + err::check_null_raise(img2, "camera read failed"); + + // FIXME: delete me and fix driver bug + uint64_t wait_us = 1000000 / _fps; + while (time::ticks_us() - _last_read_us < wait_us) { + time::sleep_us(50); + } + _last_read_us = time::ticks_us(); + return img2; + } + } + } + + void Camera::clear_buff() + { + + } + + void Camera::skip_frames(int num) + { + for(int i = 0; i < num; i++) + { + image::Image *img = read(); + delete img; + } + } + + err::Err Camera::set_resolution(int width, int height) + { + return err::ERR_NOT_IMPL; + } + + int Camera::exposure(int value) { + return -1; + } + + int Camera::gain(int value) { + return -1; + } + + int Camera::hmirror(int value) { + return -1; + } + + int Camera::vflip(int value) { + return -1; + } + + int Camera::luma(int value) { + return -1; + } + + int Camera::constrast(int value) { + return -1; + } + + int Camera::saturation(int value) { + return -1; + } + + int Camera::awb_mode(int value) { + return -1; + } + + int Camera::set_awb(int value) { + return -1; + } + + int Camera::exp_mode(int value) { + return -1; + } + + err::Err Camera::set_windowing(std::vector roi) { + return err::ERR_NOT_IMPL; + } +} + diff --git a/components/vision/port/maixcam/maix_camera_mmf.cpp b/components/vision/port/maixcam/maix_camera_mmf.cpp new file mode 100644 index 00000000..c89c4a62 --- /dev/null +++ b/components/vision/port/maixcam/maix_camera_mmf.cpp @@ -0,0 +1,723 @@ +/** + * @author neucrack@sipeed, lxowalle@sipeed + * @copyright Sipeed Ltd 2023- + * @license Apache 2.0 + * @update 2023.9.8: Add framework, create this file. + */ + + +#include "maix_camera.hpp" +#include "maix_basic.hpp" +#include "maix_i2c.hpp" +#include +#include "sophgo_middleware.hpp" + +#define MMF_SENSOR_NAME "MMF_SENSOR_NAME" +#define MAIX_SENSOR_FPS "MAIX_SENSOR_FPS" + +namespace maix::camera +{ + static bool set_regs_flag = false; + + std::vector list_devices() + { + log::warn("This device is not driven using device files!"); + return std::vector(); + } + + void set_regs_enable(bool enable) { + log::warn("This operation is not supported!"); + } + + err::Err Camera::show_colorbar(bool enable) + { + // only set variable now + // should control camera to show colorbar + _show_colorbar = enable; + return err::ERR_NONE; + } + + static void generate_colorbar(image::Image &img) + { + int width = img.width(); + int height = img.height(); + int step = width / 8; + int color_step = 255 / 7; + int color = 0; + uint8_t colors[8][3] = { + {255, 255, 255}, + {255, 0, 0}, + {255, 127, 0}, + {255, 255, 0}, + {0, 255, 0}, + {0, 0, 255}, + {143, 0, 255}, + {0, 0, 0}, + }; + for (int i = 0; i < 8; i++) + { + image::Color _color(colors[i][0], colors[i][1], colors[i][2], 0, image::FMT_RGB888); + img.draw_rect(i * step, 0, step, height, _color, -1); + color += color_step; + } + } + + Camera::Camera(int width, int height, image::Format format, const char *device, int fps, int buff_num, bool open) + { + err::Err e; + err::check_bool_raise(_check_format(format), "Format not support"); + + _width = (width == -1) ? 640 : width; + _height = (height == -1) ? 480 : height; + _format = format; + _buff_num = buff_num; + _show_colorbar = false; + _open_set_regs = set_regs_flag; + _device = ""; + _last_read_us = time::ticks_us(); + + + if (format == image::Format::FMT_RGB888 && _width * _height * 3 > 640 * 640 * 3) { + log::warn("Note that we do not recommend using large resolution RGB888 images, which can take up a lot of memory!\r\n"); + } + + // config fps + if (fps == -1 && _width <= 1280 && _height <= 720) { + _fps = 60; + } else if (fps == -1) { + _fps = 30; + } else { + _fps = fps; + } + + if ((_width > 1280 || _height > 720) && _fps > 30) { + log::warn("Current fps is too high, will be be updated to 30fps! Currently only supported up to 720p 60fps or 1440p 30fps.\r\n"); + _fps = 30; + } else if (_width <= 1280 && _height <= 720 && _fps > 60 && _fps != 80) { + log::warn("Currently only supports fixed 30,60 and 80fps in 720p configuration, current configuration will be updated to 80fps.\r\n"); + _fps = 80; + } else if (_width <= 1280 && _height <= 720 && _fps < 80 && _fps > 30 && _fps != 60) { + log::warn("Currently only supports fixed 30,60 and 80fps in 720p configuration, current configuration will be updated to 60fps.\r\n"); + _fps = 60; + } + + // open camera + if (open) { + e = this->open(_width, _height, _format, _fps, _buff_num); + err::check_raise(e, "camera open failed"); + } + } + + Camera::~Camera() + { + if (this->is_opened()) { + this->close(); + } + } + + int Camera::get_ch_nums() + { + return 2; + } + + int Camera::get_channel() + { + return this->_ch; + } + + bool Camera::_check_format(image::Format format) { + if (format == image::FMT_RGB888 || format == image::FMT_BGR888 + || format == image::FMT_RGBA8888 || format == image::FMT_BGRA8888 + || format == image::FMT_YVU420SP || format == image::FMT_GRAYSCALE) { + return true; + } else { + return false; + } + } + + static char* _get_sensor_name(void) + { + static char name[30]; + peripheral::i2c::I2C i2c_obj(4, peripheral::i2c::Mode::MASTER); + std::vector addr_list = i2c_obj.scan(); + for (size_t i = 0; i < addr_list.size(); i++) { + printf("i2c4 addr: 0x%02x\n", addr_list[i]); + switch (addr_list[i]) { + case 0x30: + printf("found sms_sc035gs, addr 0x30\n" ); + snprintf(name, sizeof(name), "sms_sc035gs"); + return name; + case 0x29: // fall through + default: + printf("found gcore_gc4653, addr 0x29\n" ); + snprintf(name, sizeof(name), "gcore_gc4653"); + return name; + } + } + + log::info("sensor address not found , use gcore_gc4653\n" ); + snprintf(name, sizeof(name), "gcore_gc4653"); + return name; + } + + static void _config_sensor_env(int fps) + { + char *env_value = getenv(MMF_SENSOR_NAME); + if (!env_value) { + char *sensor_name = _get_sensor_name(); + err::check_null_raise(sensor_name, "sensor name not found!"); + setenv(MMF_SENSOR_NAME, sensor_name, 0); + } else { + log::info("Found MMF_SENSOR_NAME=%s", env_value); + } + + env_value = getenv(MAIX_SENSOR_FPS); + if (!env_value) { + char new_value[10]; + snprintf(new_value, sizeof(new_value), "%d", fps); + setenv(MAIX_SENSOR_FPS, new_value, 0); + } else { + log::info("Found MMF_SENSOR_FPS=%s", env_value); + } + } + + err::Err Camera::open(int width, int height, image::Format format, int fps, int buff_num) + { + int width_tmp = (width == -1) ? _width : width; + int height_tmp = (height == -1) ? _height : height; + image::Format format_tmp = (format == image::FMT_INVALID) ? _format : format; + int fps_tmp = (fps == -1) ? _fps : fps; + int buff_num_tmp =( buff_num == -1) ? _buff_num : buff_num; + + // check format + err::check_bool_raise(_check_format(format_tmp), "Format not support"); + + // check if we need update camera params + if (this->is_opened()) { + this->close(); // Get new param, close and reopen + } + + _width = width_tmp; + _height = height_tmp; + _fps = fps_tmp; + _buff_num = buff_num_tmp; + _format = format_tmp; + _format_impl = _format; + + // _format_impl is used to config mmf + switch (_format) { + case image::Format::FMT_RGB888: // fall through + case image::Format::FMT_YVU420SP: + break; + case image::Format::FMT_BGR888: + _format_impl = image::Format::FMT_RGB888; + break; + case image::Format::FMT_RGBA8888: + _format_impl = image::Format::FMT_RGB888; + break; + case image::Format::FMT_BGRA8888: + _format_impl = image::Format::FMT_RGB888; + break; + case image::Format::FMT_GRAYSCALE: + _format_impl = image::Format::FMT_YVU420SP; + break; + default: + err::check_raise(err::ERR_ARGS, "Format not support"); + } + + // config sensor env + _config_sensor_env(_fps); + + // mmf init + if (!mmf_is_init()) { + mmf_sys_cfg_t sys_cfg = {0}; + if (_width <= 1280 && _height <= 720 && _fps > 30) { + sys_cfg.vb_pool[0].size = 1280 * 720 * 3 / 2; + sys_cfg.vb_pool[0].count = 3; + sys_cfg.vb_pool[0].map = 2; + sys_cfg.max_pool_cnt = 1; + } else { + sys_cfg.vb_pool[0].size = 2560 * 1440 * 3 / 2; + sys_cfg.vb_pool[0].count = 2; + sys_cfg.vb_pool[0].map = 3; + sys_cfg.max_pool_cnt = 1; + } + mmf_pre_config_sys(&sys_cfg); + err::check_bool_raise(!mmf_init(), "mmf init failed"); + } + + mmf_vi_cfg_t cfg = {0}; + cfg.w = _width; + cfg.h = _height; + cfg.fmt = mmf_invert_format_to_mmf(_format_impl); + cfg.depth = _buff_num; + cfg.fps = _fps; + if (0 != mmf_vi_init2(&cfg)) { + mmf_deinit(); + err::check_raise(err::ERR_RUNTIME, "mmf vi init failed"); + } + + err::check_bool_raise((_ch = mmf_get_vi_unused_channel()) >= 0, "mmf get vi channel failed"); + if (0 != mmf_add_vi_channel(_ch, _width, _height, mmf_invert_format_to_mmf(_format_impl))) { + mmf_vi_deinit(); + mmf_deinit(); + err::check_raise(err::ERR_RUNTIME, "mmf add vi channel failed"); + } + + _is_opened = true; + return err::ERR_NONE; + } + + void Camera::close() + { + if (this->is_closed()) + return; + + if (mmf_vi_chn_is_open(this->_ch) == true) { + if (0 != mmf_del_vi_channel(this->_ch)) { + log::error("mmf del vi channel failed"); + } + } + } + + camera::Camera *Camera::add_channel(int width, int height, image::Format format, int fps, int buff_num, bool open) + { + err::check_bool_raise(_check_format(format), "Format not support"); + + int width_tmp = (width == -1) ? _width : width; + int height_tmp = (height == -1) ? _height : height; + image::Format format_tmp = (format == image::Format::FMT_INVALID) ? _format : format; + int fps_tmp = (fps == -1) ? _fps : fps; + int buff_num_tmp = buff_num == -1 ? _buff_num : buff_num; + + Camera *cam = new Camera(width_tmp, height_tmp, format_tmp, _device.c_str(), fps_tmp, buff_num_tmp, true); + return cam; + } + + bool Camera::is_opened() + { + return _is_opened; + } + + static image::Image *_mmf_read(int ch, int width_out, int height_out, image::Format format_out, void *buff = NULL, size_t buff_size = 0) + { + image::Image *img = NULL; + uint8_t *image_data = NULL; + image::Format pop_fmt; + + void *buffer = NULL; + int buffer_len = 0, width = 0, height = 0, format = 0; + if (0 == mmf_vi_frame_pop(ch, &buffer, &buffer_len, &width, &height, &format)) { + int need_align = (width_out % mmf_vi_aligned_width(ch) == 0) ? false : true; + if (buffer == NULL) { + mmf_vi_frame_free(ch); + return NULL; + } + + if(buff) { + img = new image::Image(width_out, height_out, format_out, (uint8_t*)buff, buff_size, false); + if (buff_size < width * height * image::fmt_size[format_out]) { + log::error("camera read: buff size not enough, need %d, but %d", width * height * image::fmt_size[format_out], buff_size); + goto _error; + } + } else { + img = new image::Image(width_out, height_out, format_out); + } + + if (!img) { + log::error("camera read: new image failed"); + goto _error; + } + image_data = (uint8_t *)img->data(); + pop_fmt = (image::Format)mmf_invert_format_to_maix(format); + switch (img->format()) { + case image::Format::FMT_GRAYSCALE: + if (pop_fmt != image::Format::FMT_YVU420SP) { + log::error("camera read: format not support, need %d, but %d", image::Format::FMT_YVU420SP, pop_fmt); + goto _error; + } + if (need_align) { + for (int h = 0; h < height_out; h ++) { + memcpy((uint8_t *)image_data + h * width_out, (uint8_t *)buffer + h * width, width_out); + } + } else { + memcpy(image_data, buffer, width_out * height_out); + } + break; + case image::Format::FMT_RGB888: + if (pop_fmt != img->format()) { + log::error("camera read: format not support, need %d, but %d", img->format(), pop_fmt); + goto _error; + } + if (need_align) { + for (int h = 0; h < height_out; h++) { + memcpy((uint8_t *)image_data + h * width_out * 3, (uint8_t *)buffer + h * width * 3, width_out * 3); + } + } else { + memcpy(image_data, buffer, width_out * height_out * 3); + } + break; + case image::Format::FMT_YVU420SP: + if (pop_fmt != img->format()) { + log::error("camera read: format not support, need %d, but %d", img->format(), pop_fmt); + goto _error; + } + if (need_align) { + for (int h = 0; h < height_out * 3 / 2; h ++) { + memcpy((uint8_t *)image_data + h * width_out, (uint8_t *)buffer + h * width, width_out); + } + } else { + memcpy(image_data, buffer, width_out * height_out * 3 / 2); + } + break; + case image::Format::FMT_BGR888: + { + if (pop_fmt != image::Format::FMT_RGB888) { + log::error("camera read: format not support, need %d, but %d", image::Format::FMT_RGB888, pop_fmt); + goto _error; + } + + int bgr888_cnt = 0; + uint8_t *bgr888 = (uint8_t *)image_data; + uint8_t *rgb888 = (uint8_t *)buffer; + int line_size = width * 3; + for (int h = 0; h < height_out; h ++) { + for (int w = 0; w < width_out; w ++) { + uint8_t *tmp = rgb888 + h * line_size + w * 3; + bgr888[bgr888_cnt ++] = tmp[2]; + bgr888[bgr888_cnt ++] = tmp[1]; + bgr888[bgr888_cnt ++] = tmp[0]; + } + } + break; + } + case image::Format::FMT_RGBA8888: + { + if (pop_fmt != image::Format::FMT_RGB888) { + log::error("camera read: format not support, need %d, but %d", image::Format::FMT_RGB888, pop_fmt); + goto _error; + } + + int rgba8888_cnt = 0; + uint8_t *rgba8888 = (uint8_t *)image_data; + uint8_t *rgb888 = (uint8_t *)buffer; + int line_size = width * 3; + for (int h = 0; h < height_out; h ++) { + for (int w = 0; w < width_out; w ++) { + uint8_t *tmp = rgb888 + h * line_size + w * 3; + rgba8888[rgba8888_cnt ++] = tmp[0]; + rgba8888[rgba8888_cnt ++] = tmp[1]; + rgba8888[rgba8888_cnt ++] = tmp[2]; + rgba8888[rgba8888_cnt ++] = 0xff; + } + } + break; + } + case image::Format::FMT_BGRA8888: + { + if (pop_fmt != image::Format::FMT_RGB888) { + log::error("camera read: format not support, need %d, but %d", image::Format::FMT_RGB888, pop_fmt); + goto _error; + } + + int rgba8888_cnt = 0; + uint8_t *rgba8888 = (uint8_t *)image_data; + uint8_t *rgb888 = (uint8_t *)buffer; + int line_size = width * 3; + for (int h = 0; h < height_out; h ++) { + for (int w = 0; w < width_out; w ++) { + uint8_t *tmp = rgb888 + h * line_size + w * 3; + rgba8888[rgba8888_cnt ++] = tmp[2]; + rgba8888[rgba8888_cnt ++] = tmp[1]; + rgba8888[rgba8888_cnt ++] = tmp[0]; + rgba8888[rgba8888_cnt ++] = 0xff; + } + } + break; + } + default: + printf("Read failed, unknown format:%d\n", img->format()); + delete img; + mmf_vi_frame_free(ch); + return NULL; + } + mmf_vi_frame_free(ch); + return img; +_error: + if (img) { + delete img; + img = NULL; + } + mmf_vi_frame_free(ch); + return NULL; + } + + return img; + } // read + + image::Image *Camera::read(void *buff, size_t buff_size, bool block) + { + if (!this->is_opened()) { + err::Err e = open(_width, _height, _format, _fps, _buff_num); + err::check_raise(e, "open camera failed"); + } + + if (_show_colorbar) { + image::Image *img = new image::Image(_width, _height); + generate_colorbar(*img); + err::check_null_raise(img, "camera read failed"); + return img; + } else { + image::Image *img = _mmf_read(_ch, _width, _height, _format, buff, buff_size); + err::check_null_raise(img, "camera read failed"); + + // FIXME: delete me and fix driver bug + uint64_t wait_us = 1000000 / _fps; + while (time::ticks_us() - _last_read_us < wait_us) { + time::sleep_us(50); + } + _last_read_us = time::ticks_us(); + return img; + } + } + + void Camera::clear_buff() + { + log::warn("This operation is not supported!"); + } + + void Camera::skip_frames(int num) + { + for(int i = 0; i < num; i++) + { + image::Image *img = this->read(); + delete img; + } + } + + err::Err Camera::set_resolution(int width, int height) + { + err::Err e; + if (this->is_opened()) { + this->close(); + } + + _width = width; + _height = height; + e = this->open(_width, _height, _format, _fps, _buff_num); + err::check_raise(e, "camera open failed"); + return err::ERR_NONE; + } + + int Camera::exposure(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + uint32_t out; + if (value == -1) { + mmf_get_exptime(_ch, &out); + } else { + mmf_set_exptime(_ch, value); + out = value; + } + return out; + } + + int Camera::gain(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + uint32_t out; + if (value == -1) { + mmf_get_again(_ch, &out); + } else { + mmf_set_again(_ch, value); + out = value; + } + return out; + } + + int Camera::hmirror(int value) { + bool out; + if (value == -1) { + mmf_get_vi_hmirror(_ch, &out); + } else { + bool need_open = false; + if (this->is_opened()) { + this->close(); + need_open = true; + } + + mmf_set_vi_hmirror(_ch, value); + + if (need_open) { + err::check_raise(this->open(_width, _height, _format, _fps, _buff_num), "Open failed"); + } + out = value; + } + + return out; + } + + int Camera::vflip(int value) { + bool out; + if (value == -1) { + mmf_get_vi_vflip(_ch, &out); + } else { + bool need_open = false; + if (this->is_opened()) { + this->close(); + need_open = true; + } + + mmf_set_vi_vflip(_ch, value); + + if (need_open) { + err::check_raise(this->open(_width, _height, _format, _fps, _buff_num), "Open failed"); + } + out = value; + } + return out; + } + + int Camera::luma(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + uint32_t out; + if (value == -1) { + mmf_get_luma(_ch, &out); + } else { + mmf_set_luma(_ch, value); + out = value; + } + return out; + } + + int Camera::constrast(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + uint32_t out; + if (value == -1) { + mmf_get_constrast(_ch, &out); + } else { + mmf_set_constrast(_ch, value); + out = value; + } + return out; + } + + int Camera::saturation(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + uint32_t out; + if (value == -1) { + mmf_get_saturation(_ch, &out); + } else { + mmf_set_saturation(_ch, value); + out = value; + } + return out; + } + + int Camera::awb_mode(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + uint32_t out; + if (value == -1) { + out = mmf_get_wb_mode(_ch); + } else { + mmf_set_wb_mode(_ch, value); + out = value; + } + + err::check_bool_raise(out >= 0, "set white balance failed"); + return out; + } + + int Camera::set_awb(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + if (value == 0) { + value = 1; + } else if (value > 0) { + value = 0; + } + + return this->awb_mode(value) == 0 ? 1 : 0; + } + + int Camera::exp_mode(int value) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + uint32_t out; + if (value == -1) { + out = mmf_get_exp_mode(_ch); + } else { + mmf_set_exp_mode(_ch, value); + out = value; + } + + err::check_bool_raise(out >= 0, "set exposure failed"); + return out; + } + + err::Err Camera::set_windowing(std::vector roi) { + if (!this->is_opened()) { + return err::ERR_NOT_OPEN; + } + + int max_height, max_width; + char log_msg[100]; + int x = 0, y = 0, w = 0, h = 0; + + err::check_bool_raise(!mmf_vi_get_max_size(&max_width, &max_height), "get max size of camera failed"); + + if (roi.size() == 4) { + x = roi[0], y = roi[1], w = roi[2], h = roi[3]; + } else if (roi.size() == 2) { + w = roi[0], h = roi[1]; + x = (max_width - w) / 2; + y = (max_height - h) / 2; + } else { + err::check_raise(err::ERR_RUNTIME, "roi size must be 4 or 2"); + } + + snprintf(log_msg, sizeof(log_msg), "Width must be a multiple of 64."); + err::check_bool_raise(w % 64 == 0, std::string(log_msg)); + snprintf(log_msg, sizeof(log_msg), "the coordinate x range needs to be [0,%d].", max_width - 1); + err::check_bool_raise(x >= 0 || x < max_width, std::string(log_msg)); + snprintf(log_msg, sizeof(log_msg), "the coordinate y range needs to be [0,%d].", max_height - 1); + err::check_bool_raise(y >= 0 || y < max_height, std::string(log_msg)); + snprintf(log_msg, sizeof(log_msg), "the row of the window is larger than the maximum, try x=%d, w=%d.", x, max_width - x); + err::check_bool_raise(x + w <= max_width, std::string(log_msg)); + snprintf(log_msg, sizeof(log_msg), "the column of the window is larger than the maximum, try y=%d, h=%d.", y, max_height - y); + err::check_bool_raise(y + h <= max_height, std::string(log_msg)); + + bool is_vflip = false, is_hmirror = false; + mmf_get_vi_hmirror(_ch, &is_hmirror); + mmf_get_vi_vflip(_ch, &is_vflip); + if (!is_vflip) { + y = max_height - y - h; + } + if (!is_hmirror) { + x = max_width - x - w; + } + err::check_bool_raise(!mmf_vi_channel_set_windowing(_ch, x, y, w, h), "set windowing failed."); + return err::ERR_NONE; + } +} + diff --git a/components/vision/port/maixcam/maix_camera_mmf.hpp b/components/vision/port/maixcam/maix_camera_mmf.hpp deleted file mode 100644 index 51afa77e..00000000 --- a/components/vision/port/maixcam/maix_camera_mmf.hpp +++ /dev/null @@ -1,491 +0,0 @@ -/** - * @author lxowalle@sipeed - * @copyright Sipeed Ltd 2023- - * @license Apache 2.0 - * @update 2023.9.8: Add framework, create this file. - */ - - -#pragma once - -#include -#include "maix_err.hpp" -#include "maix_basic.hpp" -#include "maix_log.hpp" -#include "maix_image.hpp" -#include "maix_time.hpp" -#include "maix_i2c.hpp" -#include "maix_camera_base.hpp" -#include "sophgo_middleware.hpp" -#include - -static void try_deinit_mmf() -{ - static uint8_t is_called = 0; - if (!is_called) { - mmf_try_deinit(true); - is_called = 1; - } -} - -static void signal_handle(int signal) -{ - const char *signal_msg = NULL; - switch (signal) { - case SIGILL: signal_msg = "SIGILL"; break; - case SIGTRAP: signal_msg = "SIGTRAP"; break; - case SIGABRT: signal_msg = "SIGABRT"; break; - case SIGBUS: signal_msg = "SIGBUS"; break; - case SIGFPE: signal_msg = "SIGFPE"; break; - case SIGKILL: signal_msg = "SIGKILL"; break; - case SIGSEGV: signal_msg = "SIGSEGV"; break; - default: signal_msg = "UNKNOWN"; break; - } - - maix::log::error("Trigger signal, code:%s(%d)!\r\n", signal_msg, signal); - try_deinit_mmf(); - exit(1); -} - -// FIXME: move this function to port/maix_vision_maixcam.cpp ? -static __attribute__((constructor)) void maix_vision_register_signal(void) -{ - signal(SIGILL, signal_handle); - signal(SIGTRAP, signal_handle); - signal(SIGABRT, signal_handle); - signal(SIGBUS, signal_handle); - signal(SIGFPE, signal_handle); - signal(SIGKILL, signal_handle); - signal(SIGSEGV, signal_handle); - - maix::util::register_exit_function(try_deinit_mmf); -} - -#define MAIX_SENSOR_FPS "MAIX_SENSOR_FPS" -namespace maix::camera -{ - static void config_sensor(void) - { - #define MMF_SENSOR_NAME "MMF_SENSOR_NAME" - peripheral::i2c::I2C i2c_obj(4, peripheral::i2c::Mode::MASTER); - std::vector addr_list = i2c_obj.scan(); - for (size_t i = 0; i < addr_list.size(); i++) { - printf("i2c addr: 0x%02x\n", addr_list[i]); - switch (addr_list[i]) { - case 0x30: - printf("found sms_sc035gs, addr 0x30\n" ); - setenv(MMF_SENSOR_NAME, "sms_sc035gs", 0); - return; - case 0x29: // fall through - default: - printf("found gcore_gc4653, addr 0x29\n" ); - setenv(MMF_SENSOR_NAME, "gcore_gc4653", 0); - return; - } - } - - printf("Found not sensor address, use gcore_gc4653\n" ); - setenv(MMF_SENSOR_NAME, "gcore_gc4653", 0); - return; - } - - class CameraCviMmf final : public CameraBase - { - public: - CameraCviMmf(const std::string device, int width, int height, image::Format format, int buff_num, int fps) - { - this->device = device; - this->format = format; - this->width = width; - this->height = height; - this->buffer_num = buff_num; - this->ch = -1; - - config_sensor(); - - mmf_sys_cfg_t sys_cfg = {0}; - if (width <= 1280 && height <= 720 && fps > 30) { - sys_cfg.vb_pool[0].size = 1280 * 720 * 3 / 2; - sys_cfg.vb_pool[0].count = 3; - sys_cfg.vb_pool[0].map = 2; - sys_cfg.max_pool_cnt = 1; - - if (fps > 30 && fps <= 60) { - err::check_bool_raise(!setenv(MAIX_SENSOR_FPS, "60", 0), "setenv MAIX_SENSOR_FPS failed"); - } else { - err::check_bool_raise(!setenv(MAIX_SENSOR_FPS, "80", 0), "setenv MAIX_SENSOR_FPS failed"); - } - } else { - sys_cfg.vb_pool[0].size = 2560 * 1440 * 3 / 2; - sys_cfg.vb_pool[0].count = 2; - sys_cfg.vb_pool[0].map = 3; - sys_cfg.max_pool_cnt = 1; - err::check_bool_raise(!setenv(MAIX_SENSOR_FPS, "30", 0), "setenv MAIX_SENSOR_FPS failed"); - } - mmf_pre_config_sys(&sys_cfg); - - if (0 != mmf_init()) { - err::check_raise(err::ERR_RUNTIME, "mmf init failed"); - } - - mmf_vi_cfg_t cfg = {0}; - cfg.w = width; - cfg.h = height; - cfg.fmt = mmf_invert_format_to_mmf(format); - cfg.depth = buff_num; - cfg.fps = fps; - if (0 != mmf_vi_init2(&cfg)) { - mmf_deinit(); - err::check_raise(err::ERR_RUNTIME, "mmf vi init failed"); - } - } - - CameraCviMmf(const std::string device, int ch, int width, int height, image::Format format, int buff_num) - { - this->device = device; - this->format = format; - this->width = width; - this->height = height; - this->buffer_num = buff_num; - this->ch = ch; - - if (0 != mmf_init()) { - err::check_raise(err::ERR_RUNTIME, "mmf init failed"); - } - - if (0 != mmf_vi_init()) { - mmf_deinit(); - err::check_raise(err::ERR_RUNTIME, "mmf vi init failed"); - } - } - - ~CameraCviMmf() - { - mmf_del_vi_channel(this->ch); - mmf_deinit(); - } - - err::Err open(int width, int height, image::Format format, int buff_num) - { - if (format == image::FMT_GRAYSCALE) { - format = image::FMT_YVU420SP; - } - int ch = mmf_get_vi_unused_channel(); - if (ch < 0) { - log::error("camera open: mmf get vi channel failed"); - return err::ERR_RUNTIME; - } - if (0 != mmf_add_vi_channel(ch, width, height, mmf_invert_format_to_mmf(format))) { - log::error("camera open: mmf add vi channel failed"); - return err::ERR_RUNTIME; - } - - this->ch = ch; - this->width = (width == -1) ? this->width : width; - this->height = (height == -1) ? this->height : height; - this->align_width = mmf_vi_aligned_width(this->ch); - this->align_need = (this->width % this->align_width == 0) ? false : true; // Width need align only - return err::ERR_NONE; - } // open - - bool is_support_format(image::Format format) - { - if(format == image::Format::FMT_RGB888 || format == image::Format::FMT_BGR888 - || format == image::Format::FMT_YVU420SP|| format == image::Format::FMT_GRAYSCALE) - return true; - return false; - } - - void close() - { - if (mmf_vi_chn_is_open(this->ch) == true) { - if (0 != mmf_del_vi_channel(this->ch)) { - log::error("mmf del vi channel failed"); - } - } - } // close - - // read - image::Image *read(void *buff = NULL, size_t buff_size = 0) - { - image::Image *img = NULL; - - void *buffer = NULL; - int buffer_len = 0, width = 0, height = 0, format = 0; - - if (0 == mmf_vi_frame_pop(this->ch, &buffer, &buffer_len, &width, &height, &format)) { - if (buffer == NULL) { - mmf_vi_frame_free(this->ch); - return NULL; - } - if(buff) - { - if(buff_size < (size_t)buffer_len) - { - log::error("camera read: buff size not enough, need %d, but %d", buffer_len, buff_size); - mmf_vi_frame_free(this->ch); - return NULL; - } - img = new image::Image(width, height, this->format, (uint8_t*)buff, buff_size, false); - } - else - { - img = new image::Image(this->width, this->height, this->format); - } - void *image_data = img->data(); - switch (img->format()) { - case image::Format::FMT_GRAYSCALE: - if (this->align_need) { - for (int h = 0; h < this->height; h ++) { - memcpy((uint8_t *)image_data + h * this->width, (uint8_t *)buffer + h * width, this->width); - } - } else { - memcpy(image_data, buffer, this->width * this->height); - } - break; - case image::Format::FMT_BGR888: // fall through - case image::Format::FMT_RGB888: - if (this->align_need) { - for (int h = 0; h < this->height; h++) { - memcpy((uint8_t *)image_data + h * this->width * 3, (uint8_t *)buffer + h * width * 3, this->width * 3); - } - } else { - memcpy(image_data, buffer, this->width * this->height * 3); - } - break; - case image::Format::FMT_YVU420SP: - if (this->align_need) { - for (int h = 0; h < this->height * 3 / 2; h ++) { - memcpy((uint8_t *)image_data + h * this->width, (uint8_t *)buffer + h * width, this->width); - } - } else { - memcpy(image_data, buffer, this->width * this->height * 3 / 2); - } - break; - default: - printf("unknown format\n"); - delete img; - mmf_vi_frame_free(this->ch); - return NULL; - } - mmf_vi_frame_free(this->ch); - return img; - } - - return img; - } // read - - camera::CameraCviMmf *add_channel(int width, int height, image::Format format, int buff_num) - { - int new_channel = mmf_get_vi_unused_channel(); - if (new_channel < 0) { - log::error("Support not more channel!\r\n"); - return NULL; - } - return new camera::CameraCviMmf(this->device, new_channel, width, height, format, buff_num); - } - - void clear_buff() - { - - } - - bool is_opened() { - return mmf_vi_chn_is_open(this->ch); - } - - int get_ch_nums() { - return 1; - } - - int get_channel() { - return this->ch; - } - - int hmirror(int en) - { - bool out; - if (en == -1) { - mmf_get_vi_hmirror(this->ch, &out); - } else { - bool need_open = false; - if (this->is_opened()) { - this->close(); - need_open = true; - } - - mmf_set_vi_hmirror(this->ch, en); - - if (need_open) { - err::check_raise(this->open(this->width, this->height, this->format, this->buffer_num), "Open failed"); - } - out = en; - } - - return out; - } - - int vflip(int en) - { - bool out; - if (en == -1) { - mmf_get_vi_vflip(this->ch, &out); - } else { - bool need_open = false; - if (this->is_opened()) { - this->close(); - need_open = true; - } - - mmf_set_vi_vflip(this->ch, en); - - if (need_open) { - err::check_raise(this->open(this->width, this->height, this->format, this->buffer_num), "Open failed"); - } - out = en; - } - return out; - } - - int luma(int value) - { - uint32_t out; - if (value == -1) { - mmf_get_luma(this->ch, &out); - } else { - mmf_set_luma(this->ch, value); - out = value; - } - return out; - } - - int constrast(int value) - { - uint32_t out; - if (value == -1) { - mmf_get_constrast(this->ch, &out); - } else { - mmf_set_constrast(this->ch, value); - out = value; - } - return out; - } - - int saturation(int value) - { - uint32_t out; - if (value == -1) { - mmf_get_saturation(this->ch, &out); - } else { - mmf_set_saturation(this->ch, value); - out = value; - } - return out; - } - - int exposure(int value) - { - uint32_t out; - if (value == -1) { - mmf_get_exptime(this->ch, &out); - } else { - mmf_set_exptime(this->ch, value); - out = value; - } - return out; - } - - int gain(int value) - { - uint32_t out; - if (value == -1) { - mmf_get_again(this->ch, &out); - } else { - mmf_set_again(this->ch, value); - out = value; - } - return out; - } - - int awb_mode(int value) - { - uint32_t out; - if (value == -1) { - out = mmf_get_wb_mode(this->ch); - } else { - mmf_set_wb_mode(this->ch, value); - out = value; - } - - err::check_bool_raise(out >= 0, "set white balance failed"); - return out; - } - - int exp_mode(int value) - { - uint32_t out; - if (value == -1) { - out = mmf_get_exp_mode(this->ch); - } else { - mmf_set_exp_mode(this->ch, value); - out = value; - } - - err::check_bool_raise(out >= 0, "set exposure failed"); - return out; - } - - int set_windowing(std::vector roi) - { - int max_height, max_width; - char log_msg[100]; - int x = 0, y = 0, w = 0, h = 0; - - err::check_bool_raise(!mmf_vi_get_max_size(&max_width, &max_height), "get max size of camera failed"); - - if (roi.size() == 4) { - x = roi[0], y = roi[1], w = roi[2], h = roi[3]; - } else if (roi.size() == 2) { - w = roi[0], h = roi[1]; - x = (max_width - w) / 2; - y = (max_height - h) / 2; - } else { - err::check_raise(err::ERR_RUNTIME, "roi size must be 4 or 2"); - } - - snprintf(log_msg, sizeof(log_msg), "Width must be a multiple of 64."); - err::check_bool_raise(w % 64 == 0, std::string(log_msg)); - snprintf(log_msg, sizeof(log_msg), "the coordinate x range needs to be [0,%d].", max_width - 1); - err::check_bool_raise(x >= 0 || x < max_width, std::string(log_msg)); - snprintf(log_msg, sizeof(log_msg), "the coordinate y range needs to be [0,%d].", max_height - 1); - err::check_bool_raise(y >= 0 || y < max_height, std::string(log_msg)); - snprintf(log_msg, sizeof(log_msg), "the row of the window is larger than the maximum, try x=%d, w=%d.", x, max_width - x); - err::check_bool_raise(x + w <= max_width, std::string(log_msg)); - snprintf(log_msg, sizeof(log_msg), "the column of the window is larger than the maximum, try y=%d, h=%d.", y, max_height - y); - err::check_bool_raise(y + h <= max_height, std::string(log_msg)); - - // this->width = w; - // this->height = h; - err::check_bool_raise(!mmf_vi_channel_set_windowing(ch, x, y, w, h), "set windowing failed."); - - return 0; - } - private: - std::string device; - image::Format format; - int fd; - int ch; - uint32_t raw_format; - std::vector buffers; - std::vector buffers_len; - int buffer_num; - int queue_id; // user directly used buffer id - int width; - int height; - void *buff; - bool buff_alloc; - int align_width; - bool align_need; - }; - -} // namespace maix::camera diff --git a/components/vision/src/maix_camera.cpp b/components/vision/src/maix_camera.cpp deleted file mode 100644 index 28795d44..00000000 --- a/components/vision/src/maix_camera.cpp +++ /dev/null @@ -1,515 +0,0 @@ -/** - * @author neucrack@sipeed, lxowalle@sipeed - * @copyright Sipeed Ltd 2023- - * @license Apache 2.0 - * @update 2023.9.8: Add framework, create this file. - */ - - -#include "maix_camera.hpp" -#include "maix_basic.hpp" -#include -#ifdef PLATFORM_LINUX - #include "maix_camera_v4l2.hpp" -#endif -#ifdef PLATFORM_MAIXCAM - #include "maix_camera_mmf.hpp" -#endif - -namespace maix::camera -{ - static bool set_regs_flag = false; - - std::vector list_devices() - { - // find to /dev/video* - std::vector devices; - std::string path = "/dev"; - DIR *dir = opendir(path.c_str()); - if (dir == NULL) - { - return devices; - } - struct dirent *ptr; - while ((ptr = readdir(dir)) != NULL) - { - if (ptr->d_type == DT_CHR) - { - std::string name = ptr->d_name; - if (name.find("video") != std::string::npos) - { - devices.push_back( path + "/" + name ); - } - } - } - closedir(dir); - // sort devices with name - std::sort(devices.begin(), devices.end()); - // print devices - for (size_t i = 0; i < devices.size(); i++) - { - log::debug("find device: %s\n", devices[i].c_str()); - } - return devices; - } - - void set_regs_enable(bool enable) { - set_regs_flag = enable; - } - - err::Err Camera::show_colorbar(bool enable) - { - // only set variable now - // should control camera to show colorbar - _show_colorbar = enable; - return err::ERR_NONE; - } - - static void generate_colorbar(image::Image &img) - { - int width = img.width(); - int height = img.height(); - int step = width / 8; - int color_step = 255 / 7; - int color = 0; - uint8_t colors[8][3] = { - {255, 255, 255}, - {255, 0, 0}, - {255, 127, 0}, - {255, 255, 0}, - {0, 255, 0}, - {0, 0, 255}, - {143, 0, 255}, - {0, 0, 0}, - }; - for (int i = 0; i < 8; i++) - { - image::Color _color(colors[i][0], colors[i][1], colors[i][2], 0, image::FMT_RGB888); - img.draw_rect(i * step, 0, step, height, _color, -1); - color += color_step; - } - } -#ifdef PLATFORM_LINUX - static char * _get_device(const char *device) - { - if (device) - { - return (char *)device; - } - else - { - std::vector devices = list_devices(); - err::check_bool_raise(devices.size() > 0, "No camera device"); - return (char *)devices[0].c_str(); - } - } -#endif - - Camera::Camera(int width, int height, image::Format format, const char *device, int fps, int buff_num, bool open) - { - err::Err e; - err::check_bool_raise(_check_format(format), "Format not support"); - - if (format == image::Format::FMT_RGB888 && width * height * 3 > 640 * 640 * 3) { - log::warn("Note that we do not recommend using large resolution RGB888 images, which can take up a lot of memory!\r\n"); - } - - _width = (width == -1) ? 640 : width; - _height = (height == -1) ? 480 : height; - _format = format; - _buff_num = buff_num; - _show_colorbar = false; - _open_set_regs = set_regs_flag; - _impl = NULL; - -#ifdef PLATFORM_LINUX - _fps = (fps == -1) ? 30 : fps; - _device = _get_device(device); - _impl = new CameraV4L2(_device, _width, _height, _format, _buff_num); -#endif - -#ifdef PLATFORM_MAIXCAM - _last_read_us = time::ticks_us(); - if (fps == -1 && _width <= 1280 && _height <= 720) { - _fps = 60; - } else if (fps == -1) { - _fps = 30; - } else { - _fps = fps; - } - - if ((_width > 1280 || _height > 720) && _fps > 30) { - log::warn("Current fps is too high, will be be updated to 30fps! Currently only supported up to 720p 60fps or 1440p 30fps.\r\n"); - _fps = 30; - } else if (_width <= 1280 && _height <= 720 && _fps > 60 && _fps != 80) { - log::warn("Currently only supports fixed 30,60 and 80fps in 720p configuration, current configuration will be updated to 80fps.\r\n"); - _fps = 80; - } else if (_width <= 1280 && _height <= 720 && _fps < 80 && _fps > 30 && _fps != 60) { - log::warn("Currently only supports fixed 30,60 and 80fps in 720p configuration, current configuration will be updated to 60fps.\r\n"); - _fps = 60; - } - _device = ""; - _impl = new CameraCviMmf(_device, _width, _height, _format, _buff_num, _fps); -#endif - - if (open) { - e = this->open(_width, _height, _format, _fps, _buff_num); - err::check_raise(e, "camera open failed"); - } - } - - Camera::Camera(const char *device, CameraBase *base, int width, int height, image::Format format, int fps, int buff_num, bool open) - { - err::Err e; - err::check_bool_raise(_check_format(format), "Format not support"); - - _width = (width == -1) ? 640 : width; - _height = (height == -1) ? 480 : height; - _format = format; - _fps = (fps == -1) ? 30 : fps; - _buff_num = buff_num; - - _show_colorbar = false; - _open_set_regs = set_regs_flag; - _impl = base; - - if (open) { - e = this->open(_width, _height, _format, _fps, _buff_num); - err::check_raise(e, "camera open failed"); - } - } - - Camera::~Camera() - { - if (this->is_opened()) { - this->close(); - } -#ifdef PLATFORM_LINUX - delete (CameraV4L2*)_impl; -#endif - -#ifdef PLATFORM_MAIXCAM - delete (CameraCviMmf*)_impl; -#endif - } - - int Camera::get_ch_nums() - { - return 2; - } - - int Camera::get_channel() - { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->get_channel(); - } - - bool Camera::_check_format(image::Format format) { - if (format == image::FMT_RGB888 || format == image::FMT_BGR888 - || format == image::FMT_RGBA8888 || format == image::FMT_BGRA8888 - || format == image::FMT_YVU420SP || format == image::FMT_GRAYSCALE) { - return true; - } else { - return false; - } - } - - err::Err Camera::open(int width, int height, image::Format format, int fps, int buff_num) - { - if (_impl == NULL) - return err::Err::ERR_RUNTIME; - - int width_tmp = (width == -1) ? _width : width; - int height_tmp = (height == -1) ? _height : height; - image::Format format_tmp = (format == image::FMT_INVALID) ? _format : format; - int fps_tmp = (fps == -1) ? 30 : fps; - int buff_num_tmp =( buff_num == -1) ? _buff_num : buff_num; - - err::check_bool_raise(_check_format(format_tmp), "Format not support"); - - if (this->is_opened()) { - if (width == width_tmp && height == height_tmp && format == format_tmp && fps == fps_tmp && buff_num == buff_num_tmp) { - return err::ERR_NONE; - } - this->close(); // Get new param, close and reopen - } - - _width = width_tmp; - _height = height_tmp; - _fps = fps_tmp; - _buff_num = buff_num_tmp; - _format = format_tmp; - _format_impl = _format; - if(!_impl->is_support_format(_format)) - { - if(_impl->is_support_format(image::FMT_RGB888)) - _format_impl = image::FMT_RGB888; - else if(_impl->is_support_format(image::FMT_BGR888)) - _format_impl = image::FMT_BGR888; - else if(_impl->is_support_format(image::FMT_YVU420SP)) - _format_impl = image::FMT_YVU420SP; - else if(_impl->is_support_format(image::FMT_YUV420SP)) - _format_impl = image::FMT_YUV420SP; - else if(_impl->is_support_format(image::FMT_RGBA8888)) - _format_impl = image::FMT_RGBA8888; - else if(_impl->is_support_format(image::FMT_BGRA8888)) - _format_impl = image::FMT_BGRA8888; - else if(_impl->is_support_format(image::FMT_GRAYSCALE)) - _format_impl = image::FMT_GRAYSCALE; - else - return err::ERR_ARGS; - } - - return _impl->open(_width, _height, _format_impl, _buff_num);; - } - - void Camera::close() - { - if (this->is_closed()) - return; - - _impl->close(); - } - - camera::Camera *Camera::add_channel(int width, int height, image::Format format, int fps, int buff_num, bool open) - { - err::check_bool_raise(_check_format(format), "Format not support"); - - int width_tmp = (width == -1) ? _width : width; - int height_tmp = (height == -1) ? _height : height; - image::Format format_tmp = (format == image::Format::FMT_INVALID) ? _format : format; - int fps_tmp = (fps == -1) ? _fps : fps; - int buff_num_tmp = buff_num == -1 ? _buff_num : buff_num; - - Camera *cam = NULL; - if (_impl) { - CameraBase *cam_base = _impl->add_channel(width_tmp, height_tmp, format_tmp, buff_num); - err::check_bool_raise(cam_base, "Unable to add a new channel. Please check the maximum number of supported channels."); - cam = new Camera(_device.c_str(), cam_base, width_tmp, height_tmp, format_tmp, fps_tmp, buff_num_tmp, open); - } - return cam; - } - - bool Camera::is_opened() - { - if (_impl == NULL) - return false; - - return _impl->is_opened(); - } - - image::Image *Camera::read(void *buff, size_t buff_size, bool block) - { - if (!this->is_opened()) { - err::Err e = open(_width, _height, _format, _buff_num); - err::check_raise(e, "open camera failed"); - } - - if (_show_colorbar) { - image::Image *img = new image::Image(_width, _height); - generate_colorbar(*img); - err::check_null_raise(img, "camera read failed"); - return img; - } else { - // it's better all done by impl to faster read, but if impl not support, we have to convert it - if(_format_impl == _format) - { - image::Image *img = _impl->read(buff, buff_size); - err::check_null_raise(img, "camera read failed"); - - // FIXME: delete me and fix driver bug - uint64_t wait_us = 1000000 / _fps; - while (time::ticks_us() - _last_read_us < wait_us) { - time::sleep_us(50); - } - _last_read_us = time::ticks_us(); - return img; - } - else - { - image::Image *img = _impl->read(); - image::Image *img2 = img->to_format(_format, buff, buff_size); - delete img; - err::check_null_raise(img2, "camera read failed"); - - // FIXME: delete me and fix driver bug - uint64_t wait_us = 1000000 / _fps; - while (time::ticks_us() - _last_read_us < wait_us) { - time::sleep_us(50); - } - _last_read_us = time::ticks_us(); - return img2; - } - } - } - - void Camera::clear_buff() - { - if (_impl == NULL) - return; - _impl->clear_buff(); - } - - void Camera::skip_frames(int num) - { - if (_impl == NULL) - return; - for(int i = 0; i < num; i++) - { - image::Image *img = _impl->read(); - delete img; - } - } - - err::Err Camera::set_resolution(int width, int height) - { - err::Err e; - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (this->is_opened()) { - this->close(); - } - - _width = width; - _height = height; - e = this->open(_width, _height, _format, _fps, _buff_num); - err::check_raise(e, "camera open failed"); - return err::ERR_NONE; - } - - int Camera::exposure(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->exposure((uint32_t)value); - } - - int Camera::gain(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->gain((uint32_t)value); - } - - int Camera::hmirror(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->hmirror(value); - } - - int Camera::vflip(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->vflip(value); - } - - int Camera::luma(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->luma(value); - } - - int Camera::constrast(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->constrast(value); - } - - int Camera::saturation(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->saturation(value); - } - - int Camera::awb_mode(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->awb_mode(value); - } - - int Camera::set_awb(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - if (value == 0) { - value = 1; - } else if (value > 0) { - value = 0; - } - - return _impl->awb_mode(value) == 0 ? 1 : 0; - } - - int Camera::exp_mode(int value) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - return _impl->exp_mode(value); - } - - err::Err Camera::set_windowing(std::vector roi) { - if (_impl == NULL) - return err::ERR_NOT_INIT; - - if (!this->is_opened()) { - return err::ERR_NOT_OPEN; - } - - err::check_bool_raise(!_impl->set_windowing(roi), "set_windowing failed"); - return err::ERR_NONE; - } -} - diff --git a/examples/camera_display/main/src/main.cpp b/examples/camera_display/main/src/main.cpp index 83392c76..3ed95195 100644 --- a/examples/camera_display/main/src/main.cpp +++ b/examples/camera_display/main/src/main.cpp @@ -16,19 +16,42 @@ using namespace maix; static int cmd_init(void); static int cmd_loop(camera::Camera *cam); +static bool camera2_enable = false; + int _main(int argc, char* argv[]) { uint64_t t1, t2, t3; float fps = 0; char buf[128] = {0}; - camera::Camera cam = camera::Camera(); - display::Display screen = display::Display(); - log::info("camera and display open success\n"); - log::info("camera size: %dx%d\n", cam.width(), cam.height()); - log::info("screen size: %dx%d\n", screen.width(), screen.height()); + int cam_w = -1; + int cam_h = -1; + image::Format cam_fmt = image::Format::FMT_RGB888; + int cam_fps = -1; + int cam_buffer_num = 3; + if (argc > 1) { + if (!strcmp(argv[1], "-h")) { + log::info("./camera_display "); + log::info("example: ./camera_display 640 480 0 60 2"); + exit(0); + } else { + cam_w = atoi(argv[1]); + } + } + if (argc > 2) cam_h = atoi(argv[2]); + if (argc > 3) cam_fmt = (image::Format)atoi(argv[3]); + if (argc > 4) cam_fps = atoi(argv[4]); + if (argc > 5) cam_buffer_num = atoi(argv[5]); + log::info("Camera width:%d height:%d format:%s fps:%d buffer_num:%d", cam_w, cam_h, image::fmt_names[cam_fmt].c_str(), cam_fps, cam_buffer_num); - err::check_bool_raise(!cmd_init(), "cmd init failed!\r\n"); + camera::Camera cam = camera::Camera(cam_w, cam_h, cam_fmt, "", cam_fps, cam_buffer_num); + camera::Camera *cam2 = NULL; + display::Display screen = display::Display(); + log::info("camera and display open success"); + log::info("camera size: %dx%d", cam.width(), cam.height()); + log::info("screen size: %dx%d", screen.width(), screen.height()); + cam.skip_frames(5); + err::check_bool_raise(!cmd_init(), "cmd init failed!"); while(!app::need_exit()) { @@ -40,20 +63,32 @@ int _main(int argc, char* argv[]) // read image from camera image::Image *img = cam.read(); - err::check_null_raise(img, "camera read failed"); - // time when read image finished - t2 = time::ticks_ms(); - - // draw fps on image - img->draw_string(0, 10, buf, image::Color::from_rgb(255, 0, 0), 1.5); + if (camera2_enable) { + if (!cam2) { + cam2 = cam.add_channel(cam.width(), cam.height()); + err::check_null_raise(cam2, "camera add channel failed!"); + } + image::Image *img2 = cam2->read(); + if (img2->format() == image::Format::FMT_RGB888) { + image::Image *resize_img2 = img2->resize(img->width()/2, img->height()/2); + img->draw_image(0, 0, *resize_img2); + delete resize_img2; + } + delete img2; + } else { + if (cam2) { + delete cam2; + cam2 = NULL; + } + } - // check if screen is closed by user(mostly for PC), and show image on screen - if(!screen.is_opened()) - { - log::info("screen closed\n"); - break; + if (img->format() == image::Format::FMT_RGB888) { + img->draw_string(0, 10, buf, image::COLOR_GREEN, 1.5); } + + // time when read image finished + t2 = time::ticks_ms(); screen.show(*img); // free image data, important! @@ -65,6 +100,10 @@ int _main(int argc, char* argv[]) snprintf(buf, sizeof(buf), "cam: %ld, disp: %ld, all: %ld (ms), fps: %.2f", t2-t1, t3-t2, t3-t1, fps); } + if (cam2) { + delete cam2; + cam2 = NULL; + } return 0; } @@ -85,13 +124,19 @@ static int cmd_init(void) printf( "========================\r\n" "Intput param:\r\n" - "0 : set exposure\r\n" + "0 : set exposure, unit:us\r\n" "1 : set gain\r\n" "2 : set luma\r\n" "3 : set constrast\r\n" "4 : set saturation\r\n" "5 : set white balance mode\r\n" "6 : set exposure mode\r\n" + "7 : show colorbar, 1:enable 0:disable\r\n" + "8 : set new resolution\r\n" + "9 : set hmirror, 1:enable;0:disable\r\n" + "10 : set vflip, 1:enable;0:disable\r\n" + "11 : set windowing\r\n" + "12 : use a new channel of camera, 1:use 0:unuse\r\n" "========================\r\n"); fflush(stdin); return 0; @@ -100,7 +145,7 @@ static int cmd_init(void) static int cmd_loop(camera::Camera *cam) { uint64_t t1; - uint64_t value = -1; + uint64_t value = -1, value2 = -1, value3 = -1, value4 = -1; int cmd = -1; if (!cam) { @@ -108,8 +153,10 @@ static int cmd_loop(camera::Camera *cam) return -1; } - if (scanf("%d %ld\r\n", &cmd, &value) > 0) { - log::info("cmd:%d %ld\r\n", cmd, value); + int len = scanf("%d %ld %ld %ld %ld\r\n", &cmd, &value, &value2, &value3, &value4); + if (len > 0) { + log::info("len:%d cmd:%d value:%ld %ld %ld %ld", len, cmd, value, value2, value3, value4); + fflush(stdin); t1 = time::ticks_ms(); switch (cmd) { case 0: @@ -165,10 +212,10 @@ static int cmd_loop(camera::Camera *cam) case 5: { uint32_t out = 0; - out = cam->awb_mode(value); + out = cam->set_awb(value); err::check_bool_raise(out == value, "set error"); log::info("set white balance mode: %ld\r\n", value); - out = cam->awb_mode(); + out = cam->set_awb(); log::info("get white balance mode: %d\r\n", out); } break; @@ -180,10 +227,46 @@ static int cmd_loop(camera::Camera *cam) log::info("set exposure mode: %ld\r\n", value); out = cam->exp_mode(); log::info("get exposure mode: %d\r\n", out); + break; + } + case 7: + { + err::check_raise(cam->show_colorbar(value), "set error"); + break; + } + case 8: + { + err::check_raise(cam->set_resolution(value, value2), "set error"); + break; + } + case 9: + { + cam->hmirror(value); + log::info("set hmirror: %d", value); + log::info("get hmirror: %d", cam->hmirror()); + break; + } + case 10: + { + cam->vflip(value); + log::info("set vflip: %d", value); + log::info("get vflip: %d", cam->vflip()); + break; + } + case 11: + { + std::vector roi = {(int)value, (int)value2, (int)value3, (int)value4}; + err::check_raise(cam->set_windowing(roi), "set error"); + break; + } + case 12: + { + camera2_enable = value; + break; } - break; default:printf("Find not cmd!\r\n"); break; } + log::info("cmd use %ld ms\r\n", time::ticks_ms() - t1); fflush(stdin); }