Repository: nifi-minifi-cpp Updated Branches: refs/heads/master 7958bd11b -> ebdfc21f8
MINIFICPP-475 Added support for preferred frame width/height to GetUSBCamera properties This closes #318. Signed-off-by: Marc Parisi <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/commit/ca3b1700 Tree: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/tree/ca3b1700 Diff: http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/diff/ca3b1700 Branch: refs/heads/master Commit: ca3b1700e4f0c448862df41baab100fb42f8070f Parents: 7958bd1 Author: Andrew I. Christianson <[email protected]> Authored: Thu May 3 14:37:55 2018 -0400 Committer: Marc Parisi <[email protected]> Committed: Thu May 3 17:10:25 2018 -0400 ---------------------------------------------------------------------- PROCESSORS.md | 10 ++- extensions/usb-camera/GetUSBCamera.cpp | 116 +++++++++++++++++++++++----- extensions/usb-camera/GetUSBCamera.h | 2 + 3 files changed, 106 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/ca3b1700/PROCESSORS.md ---------------------------------------------------------------------- diff --git a/PROCESSORS.md b/PROCESSORS.md index f88eb98..e6213ef 100644 --- a/PROCESSORS.md +++ b/PROCESSORS.md @@ -331,8 +331,12 @@ flow files upon capture. The onTrigger of this processor is a NOOP and will report an error if inputs are flowed into the processor. Because of this, the standard event/timer driven scheduling options have no effect. -If the camera supports multiple image size/quality settings, the highest -quality is chosen for the given FPS. For example: +If the Width/Height properties are set, the closest supported image frame +dimensions to the given Width/Height properties are used. + +If no Width/Height properties are set, and the camera supports multiple image +size/quality settings, the highest quality is chosen for the given FPS. For +example: - If the FPS is 10 and the camera supports a maximum of 1920x1080 at this FPS, output images will be 1920x780 @@ -348,6 +352,8 @@ default values, and whether a property supports the NiFi Expression Language. | Name | Default Value | Allowable Values | Description | | - | - | - | - | | **FPS** | 1 | | Frames per second to capture from USB camera | +| Width | | | Desired frame width (closest supported by camera hardware will be used) | +| Height | | | Desired frame height (closest supported by camera hardware will be used) | | **Format** | PNG | PNG, RAW | Frame format (currently only PNG and RAW are supported; RAW is a binary pixel buffer of RGB values) | | USB Vendor ID | | | USB Vendor ID of camera device, in hexadecimal format | | USB Product ID | | | USB Product ID of camera device, in hexadecimal format| http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/ca3b1700/extensions/usb-camera/GetUSBCamera.cpp ---------------------------------------------------------------------- diff --git a/extensions/usb-camera/GetUSBCamera.cpp b/extensions/usb-camera/GetUSBCamera.cpp index 09699bd..c5518a6 100644 --- a/extensions/usb-camera/GetUSBCamera.cpp +++ b/extensions/usb-camera/GetUSBCamera.cpp @@ -31,8 +31,14 @@ namespace processors { core::Property GetUSBCamera::FPS( // NOLINT "FPS", "Frames per second to capture from USB camera", "1"); +core::Property GetUSBCamera::Width( // NOLINT + "Width", "Target width of image to capture from USB camera", ""); +core::Property GetUSBCamera::Height( // NOLINT + "Height", "Target height of image to capture from USB camera", ""); core::Property GetUSBCamera::Format( // NOLINT - "Format", "Frame format (currently only PNG and RAW are supported; RAW is a binary pixel buffer of RGB values)", "PNG"); + "Format", + "Frame format (currently only PNG and RAW are supported; RAW is a binary pixel buffer of RGB values)", + "PNG"); core::Property GetUSBCamera::VendorID( // NOLINT "USB Vendor ID", "USB Vendor ID of camera device, in hexadecimal format", "0x0"); core::Property GetUSBCamera::ProductID( // NOLINT @@ -47,6 +53,8 @@ core::Relationship GetUSBCamera::Failure( // NOLINT void GetUSBCamera::initialize() { std::set<core::Property> properties; properties.insert(FPS); + properties.insert(Width); + properties.insert(Height); properties.insert(Format); properties.insert(VendorID); properties.insert(ProductID); @@ -97,12 +105,18 @@ void GetUSBCamera::onFrame(uvc_frame_t *frame, void *ptr) { std::shared_ptr<OutputStreamCallback> write_cb; if (cb_data->format == "PNG") { - write_cb = std::make_shared<GetUSBCamera::PNGWriteCallback>(cb_data->png_write_mtx, cb_data->frame_buffer, cb_data->device_width, cb_data->device_height); + write_cb = std::make_shared<GetUSBCamera::PNGWriteCallback>(cb_data->png_write_mtx, + cb_data->frame_buffer, + cb_data->device_width, + cb_data->device_height); } else if (cb_data->format == "RAW") { write_cb = std::make_shared<GetUSBCamera::RawWriteCallback>(cb_data->frame_buffer); } else { cb_data->logger->log_warn("Invalid format specified (%s); defaulting to PNG", cb_data->format); - write_cb = std::make_shared<GetUSBCamera::PNGWriteCallback>(cb_data->png_write_mtx, cb_data->frame_buffer, cb_data->device_width, cb_data->device_height); + write_cb = std::make_shared<GetUSBCamera::PNGWriteCallback>(cb_data->png_write_mtx, + cb_data->frame_buffer, + cb_data->device_width, + cb_data->device_height); } session->write(flow_file, write_cb.get()); @@ -133,6 +147,34 @@ void GetUSBCamera::onSchedule(core::ProcessContext *context, core::ProcessSessio } } + uint16_t target_width = 0; + uint16_t target_height = 0; + std::string conf_width_str; + context->getProperty("Width", conf_width_str); + + if (!conf_width_str.empty()) { + auto target_width_ul = std::stoul(conf_width_str); + if (target_width_ul > UINT16_MAX) { + logger_->log_error("Configured target width %s is out of range", conf_width_str); + } else { + target_width = static_cast<uint16_t>(target_width_ul); + } + logger_->log_info("Using configured target width: %i", target_width); + } + + std::string conf_height_str; + context->getProperty("Height", conf_height_str); + + if (!conf_height_str.empty()) { + auto target_height_ul = std::stoul(conf_height_str); + if (target_height_ul > UINT16_MAX) { + logger_->log_error("Configured target height %s is out of range", conf_height_str); + } else { + target_height = static_cast<uint16_t>(target_height_ul); + } + logger_->log_info("Using configured target height: %i", target_height); + } + std::string conf_format_str; context->getProperty("Format", conf_format_str); @@ -160,7 +202,7 @@ void GetUSBCamera::onSchedule(core::ProcessContext *context, core::ProcessSessio cleanupUvc(); logger_->log_info("Beginning to capture frames from USB camera"); - uvc_stream_ctrl_t ctrl { }; + uvc_stream_ctrl_t ctrl{}; uvc_error_t res; res = uvc_init(&ctx_, nullptr); @@ -196,6 +238,11 @@ void GetUSBCamera::onSchedule(core::ProcessContext *context, core::ProcessSessio uint32_t max_size = 0; uint32_t fps = 0; + double min_diff = -1; + double current_diff = -1; + double current_width_diff = -1; + double current_height_diff = -1; + for (auto fmt_desc = uvc_get_format_descs(devh_); fmt_desc; fmt_desc = fmt_desc->next) { uvc_frame_desc_t *frame_desc; switch (fmt_desc->bDescriptorSubtype) { @@ -203,19 +250,45 @@ void GetUSBCamera::onSchedule(core::ProcessContext *context, core::ProcessSessio case UVC_VS_FORMAT_FRAME_BASED: for (frame_desc = fmt_desc->frame_descs; frame_desc; frame_desc = frame_desc->next) { uint32_t frame_fps = 10000000 / frame_desc->dwDefaultFrameInterval; - if (frame_desc->dwMaxVideoFrameBufferSize > max_size && frame_fps >= target_fps) { - width = frame_desc->wWidth; - height = frame_desc->wHeight; - max_size = frame_desc->dwMaxVideoFrameBufferSize; - fps = frame_fps; + logger_->log_info("Discovered supported format %ix%i @ %i", + frame_desc->wWidth, + frame_desc->wHeight, + frame_fps); + if (target_height > 0 && target_width > 0) { + if (frame_fps >= target_fps) { + current_width_diff = abs(frame_desc->wWidth - target_width) / static_cast<double>(target_width); + logger_->log_debug("Current frame format width difference is %f", current_width_diff); + current_height_diff = abs(frame_desc->wHeight - target_height) / static_cast<double>(target_height); + logger_->log_debug("Current frame format height difference is %f", current_height_diff); + current_diff = (current_width_diff + current_height_diff) / 2; + logger_->log_debug("Current frame format difference is %f", current_diff); + + if (min_diff < 0 || current_diff < min_diff) { + logger_->log_info("Format %ix%i @ %i is now closest to target", + frame_desc->wWidth, + frame_desc->wHeight, + frame_fps); + width = frame_desc->wWidth; + height = frame_desc->wHeight; + max_size = frame_desc->dwMaxVideoFrameBufferSize; + fps = frame_fps; + min_diff = current_diff; + } + } + + } else { + if (frame_desc->dwMaxVideoFrameBufferSize > max_size && frame_fps >= target_fps) { + width = frame_desc->wWidth; + height = frame_desc->wHeight; + max_size = frame_desc->dwMaxVideoFrameBufferSize; + fps = frame_fps; + } } } - case UVC_VS_FORMAT_MJPEG: - logger_->log_info("Skipping MJPEG frame formats"); + case UVC_VS_FORMAT_MJPEG:logger_->log_info("Skipping MJPEG frame formats"); - default: - logger_->log_warn("Found unknown format"); + default:logger_->log_warn("Found unknown format"); } } @@ -313,7 +386,10 @@ void GetUSBCamera::onTrigger(core::ProcessContext *context, core::ProcessSession } } -GetUSBCamera::PNGWriteCallback::PNGWriteCallback(std::shared_ptr<std::mutex> write_mtx, uvc_frame_t *frame, uint32_t width, uint32_t height) +GetUSBCamera::PNGWriteCallback::PNGWriteCallback(std::shared_ptr<std::mutex> write_mtx, + uvc_frame_t *frame, + uint32_t width, + uint32_t height) : png_write_mtx_(std::move(write_mtx)), frame_(frame), width_(width), @@ -346,15 +422,15 @@ int64_t GetUSBCamera::PNGWriteCallback::process(std::shared_ptr<io::BaseStream> try { png_set_write_fn(png, this, [](png_structp out_png, - png_bytep out_data, - png_size_t num_bytes) { - auto this_callback = reinterpret_cast<PNGWriteCallback *>(png_get_io_ptr(out_png)); - std::copy(out_data, out_data + num_bytes, std::back_inserter(this_callback->png_output_buf_)); - }, + png_bytep out_data, + png_size_t num_bytes) { + auto this_callback = reinterpret_cast<PNGWriteCallback *>(png_get_io_ptr(out_png)); + std::copy(out_data, out_data + num_bytes, std::back_inserter(this_callback->png_output_buf_)); + }, [](png_structp flush_png) {}); png_set_IHDR(png, info, width_, height_, 8, - PNG_COLOR_TYPE_RGB, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/ca3b1700/extensions/usb-camera/GetUSBCamera.h ---------------------------------------------------------------------- diff --git a/extensions/usb-camera/GetUSBCamera.h b/extensions/usb-camera/GetUSBCamera.h index 4cc2b81..4c1c572 100644 --- a/extensions/usb-camera/GetUSBCamera.h +++ b/extensions/usb-camera/GetUSBCamera.h @@ -61,6 +61,8 @@ class GetUSBCamera : public core::Processor { } static core::Property FPS; + static core::Property Width; + static core::Property Height; static core::Property Format; static core::Property VendorID; static core::Property ProductID;
