OFX, hay còn gọi là API xử lý hình ảnh OFX , là một tiêu chuẩn mở để tạo hiệu ứng hình ảnh 2D và tổng hợp video. Nó hoạt động theo mô hình phát triển ứng dụng giống như plugin. Về cơ bản, nó đóng vai trò vừa là Máy chủ - một ứng dụng cung cấp một tập hợp các phương thức, vừa là một Plug-in - một ứng dụng hoặc mô-đun triển khai tập hợp này.
Cấu hình này mang lại khả năng mở rộng không giới hạn chức năng của ứng dụng máy chủ.
Các ứng dụng như Final Cut X và DaVinci Resolve Studio, bắt đầu từ phiên bản 16, hỗ trợ đầy đủ các đường dẫn Apple Metal. Tương tự như OpenCL và Cuda, trong trường hợp OFX, bạn có thể lấy bộ mô tả hoặc trình xử lý hàng đợi lệnh dành riêng cho nền tảng. Hệ thống máy chủ cũng chịu trách nhiệm phân bổ một nhóm các hàng đợi như vậy và cân bằng các phép tính trên chúng.
Hơn nữa, nó đặt dữ liệu clip hình ảnh nguồn và đích vào bộ nhớ GPU, đơn giản hóa đáng kể việc phát triển chức năng mở rộng.
Với Resolve, mọi thứ phức tạp hơn một chút. DaVinci thông báo hỗ trợ OFX v1.4, mặc dù có một số hạn chế. Cụ thể, một số phương pháp làm việc với các chức năng giao diện không có sẵn để sử dụng. Để xác định phương thức nào khả dụng, OFX cho phép bạn kiểm tra bộ được hỗ trợ thông qua các truy vấn khóa/giá trị.
Phương pháp xuất bản trong mã plugin dựa trên lệnh gọi C. Nhưng chúng tôi sẽ sử dụng shell OpenFXS C++ được điều chỉnh cho C++17. Để thuận tiện, tôi đã biên soạn mọi thứ vào một kho lưu trữ: dehancer-external được lấy từ dự án Dehancer mã nguồn mở.
Trong dự án này, tôi sẽ sử dụng OpenFXS, một tiện ích mở rộng C++ cho OpenFX do Bruno Nicoletti viết ban đầu và đã trở nên phổ biến theo thời gian trong các dự án xử lý video thương mại và nguồn mở.
OpenFXS ban đầu không được điều chỉnh cho phù hợp với các phương ngữ C++ hiện đại, vì vậy tôi đã cập nhật nó để tương thích với C++17 .
OFX, và do đó là OFXS, là một mô-đun phần mềm độc lập được chương trình máy chủ tải động. Về cơ bản, nó là một thư viện động được tải khi ứng dụng chính khởi động. OpenFXS, giống như OFX, phải xuất bản chữ ký phương thức. Do đó, chúng tôi sử dụng một phương thức C từ mã.
Để bắt đầu phát triển trong OpenFXS, bạn cần đồng ý với một số nhóm lớp phổ biến được sử dụng để tạo chức năng mới trong ứng dụng của bạn. Thông thường, trong một dự án mới, bạn cần kế thừa từ các lớp này và triển khai hoặc ghi đè một số phương thức ảo.
Để tạo plugin của riêng bạn trên hệ thống máy chủ, hãy bắt đầu bằng cách làm quen với các lớp công khai sau và phương thức tương tự:
Một đặc điểm giúp phân biệt quá trình quay video với việc chỉ chụp một hình ảnh trong ảnh là sự thay đổi động của cảnh và ánh sáng của cả cảnh nói chung và các khu vực trong ảnh. Điều này xác định cách kiểm soát độ phơi sáng trong quá trình chụp.
Trong video kỹ thuật số, có một chế độ giám sát điều khiển dành cho người vận hành trong đó mức độ phơi sáng của các khu vực được ánh xạ thành một tập hợp giới hạn các vùng, mỗi vùng có màu riêng.
Chế độ này đôi khi được gọi là chế độ "kẻ săn mồi" hoặc chế độ Màu sai. Thang đo thường được tham chiếu đến thang đo IRE.
Màn hình như vậy cho phép bạn xem các vùng phơi sáng và tránh những sai lầm đáng kể khi cài đặt các thông số chụp của máy ảnh. Một cái gì đó có ý nghĩa tương tự được sử dụng khi phơi sáng trong nhiếp ảnh - ví dụ như phân vùng theo Adams.
Bạn có thể đo một mục tiêu cụ thể bằng máy đo độ phơi sáng và xem mục tiêu đó nằm ở vùng nào và trong thời gian thực, chúng tôi nhìn thấy các vùng đó, được tô màu gọn gàng để dễ nhận biết.
Số lượng vùng được xác định bởi mục tiêu và khả năng của màn hình điều khiển. Ví dụ: màn hình được sử dụng với máy ảnh Arri Alexa có thể kết hợp tối đa 6 vùng.
Trước khi tiếp tục với ví dụ, chúng ta cần thêm một số lớp proxy đơn giản để triển khai OpenFXS làm nền tảng xử lý dữ liệu nguồn, chẳng hạn như Kết cấu kim loại. Các lớp này bao gồm:
immetalling::Image2Texture : Một functor để truyền dữ liệu từ bộ đệm clip sang kết cấu Kim loại. Từ DaVinci, bạn có thể trích xuất bộ đệm có cấu trúc và gói giá trị kênh hình ảnh bất kỳ vào plugin và nó sẽ được trả về ở dạng tương tự.
Để làm việc với định dạng luồng trong OFX dễ dàng hơn, bạn có thể yêu cầu máy chủ chuẩn bị trước dữ liệu thuộc loại cụ thể. Tôi sẽ sử dụng các phao được đóng gói trong RGBA - đỏ/xanh/xanh/alpha.
Chúng tôi kế thừa các lớp cơ sở OFXS và viết chức năng của mình mà không đi sâu vào chi tiết về cách hoạt động của lõi Metal:
Ngoài ra, chúng ta sẽ cần một số lớp tiện ích được xây dựng trên Metal để phân tách hợp lý mã máy chủ và mã hạt nhân trên MSL. Bao gồm các:
immetalling::FalseColorKernel : Lớp chức năng chính của chúng tôi, một trình mô phỏng "kẻ săn mồi" áp phích (mẫu xuống) thành một số màu được chỉ định.
Mã hạt nhân cho chế độ "kẻ săn mồi" có thể trông như thế này:
static constant float3 kIMP_Y_YUV_factor = {0.2125, 0.7154, 0.0721}; constexpr sampler baseSampler(address::clamp_to_edge, filter::linear, coord::normalized); inline float when_eq(float x, float y) { return 1.0 - abs(sign(x - y)); } static inline float4 sampledColor( texture2d<float, access::sample> inTexture, texture2d<float, access::write> outTexture, uint2 gid ){ float w = outTexture.get_width(); return mix(inTexture.sample(baseSampler, float2(gid) * float2(1.0/(w-1.0), 1.0/float(outTexture.get_height()-1))), inTexture.read(gid), when_eq(inTexture.get_width(), w) // whe equal read exact texture color ); } kernel void kernel_falseColor( texture2d<float, access::sample> inTexture [[texture(0)]], texture2d<float, access::write> outTexture [[texture(1)]], device float3* color_map [[ buffer(0) ]], constant uint& level [[ buffer(1) ]], uint2 gid [[thread_position_in_grid]]) { float4 inColor = sampledColor(inTexture,outTexture,gid); float luminance = dot(inColor.rgb, kIMP_Y_YUV_factor); uint index = clamp(uint(luminance*(level-1)),uint(0),uint(level-1)); float4 color = float4(1); if (index<level) color.rgb = color_map[index]; outTexture.write(color,gid); }
Khởi tạo Plugin OFX
Chúng ta sẽ bắt đầu bằng việc định nghĩa lớp imetalling::falsecolor::Factory.
Trong lớp này, chúng ta sẽ đặt một tham số duy nhất - trạng thái của màn hình (bật hoặc tắt). Điều này là cần thiết cho ví dụ của chúng tôi.
Chúng ta sẽ kế thừa từ OFX::PluginFactoryHelper
và nạp chồng 5 phương thức:
ImageEffectDescriptor
.
mô tảInContext(ImageEffectDescriptor&,ContextEnum) : Tương tự như phương thức describe
, phương thức này cũng được gọi khi plugin được tải và phải được xác định trong lớp của chúng ta. Nó sẽ xác định các thuộc tính liên quan đến bối cảnh hiện tại.
Ngữ cảnh xác định loại hoạt động mà ứng dụng hoạt động, chẳng hạn như bộ lọc, sơn, hiệu ứng chuyển tiếp hoặc bộ đếm thời gian khung trong clip.
ImageEffect
. Nói cách khác, imetalling::falsecolor::Plugin
của chúng tôi trong đó chúng tôi đã xác định tất cả các chức năng, cả về các sự kiện của người dùng trong chương trình máy chủ và hiển thị (chuyển đổi) khung nguồn thành khung đích: OFX::ImageEffect *Factory::createInstance(OfxImageEffectHandle handle,OFX::ContextEnum) { return new Plugin(handle); }
Ở giai đoạn này, nếu bạn biên dịch một gói bằng mô-đun OFX, thì plugin sẽ có sẵn trong ứng dụng máy chủ và trong DaVinci, nó có thể được tải lên nút chỉnh sửa.
Tuy nhiên, để hoạt động hoàn toàn với một phiên bản plugin, bạn cần xác định ít nhất phần tương tác và phần liên quan đến việc xử lý luồng video đến.
Để thực hiện điều này, chúng ta kế thừa từ lớp OFX::ImageEffect và nạp chồng các phương thức ảo:
đã thay đổiParam(const OFX::InstanceChangedArgs&, const std::string&) - Phương thức này cho phép chúng ta xác định logic để xử lý sự kiện. Loại sự kiện được xác định bởi giá trị của OFX::InstanceChangedArgs::reason và có thể là: eChangeUserEdit, eChangePluginEdit, eChangeTime - sự kiện xảy ra do thuộc tính được người dùng chỉnh sửa, thay đổi trong plugin hoặc ứng dụng máy chủ hoặc là kết quả của sự thay đổi trong dòng thời gian.
Tham số thứ hai chỉ định tên chuỗi mà chúng tôi đã xác định ở giai đoạn khởi tạo plugin, trong trường hợp của chúng tôi, đó là một tham số: false_color_enabled_check_box .
Bạn có thể đọc cách triển khai tương tác tương tác với OFX trong mã Interaction.cpp . Như bạn có thể thấy, chúng ta nhận được các con trỏ tới các clip: đoạn nguồn và vùng bộ nhớ mà chúng ta sẽ đặt phép biến đổi đích trong đó.
Chúng tôi sẽ thêm một lớp logic khác mà trên đó chúng tôi sẽ xác định tất cả logic để khởi chạy quá trình chuyển đổi. Trong trường hợp của chúng tôi, đây là phương pháp duy nhất để ghi đè cho đến nay:
Ở giai đoạn khởi chạy, chúng tôi đã có sẵn một đối tượng với các thuộc tính hữu ích: chúng tôi có ít nhất một con trỏ tới luồng video (chính xác hơn là vùng bộ nhớ có dữ liệu hình ảnh khung) và quan trọng nhất là một hàng lệnh Metal.
Bây giờ, chúng ta có thể xây dựng một lớp chung sẽ đưa chúng ta đến gần hơn với một dạng đơn giản của việc sử dụng lại mã hạt nhân. Tiện ích mở rộng OpenFXS đã có một lớp như vậy: OFX::ImageProcessor; chúng ta chỉ cần làm quá tải nó.
Trong hàm tạo, nó có tham số OFX::ImageEffect, tức là trong đó, chúng ta sẽ không chỉ nhận được trạng thái hiện tại của các tham số plugin mà còn mọi thứ cần thiết để làm việc với GPU.
Ở giai đoạn này, chúng ta chỉ cần nạp chồng phương thức processImagesMetal() và bắt đầu xử lý các kernel đã được triển khai trên Metal.
Processor::Processor( OFX::ImageEffect *instance, OFX::Clip *source, OFX::Clip *destination, const OFX::RenderArguments &args, bool enabled ) : OFX::ImageProcessor(*instance), enabled_(enabled), interaction_(instance), wait_command_queue_(false), /// grab the current frame of a clip from OFX host memory source_(source->fetchImage(args.time)), /// create a target frame of a clip with the memory area already specified in OFX destination_(destination->fetchImage(args.time)), source_container_(nullptr), destination_container_(nullptr) { /// Set OFX rendering arguments to GPU setGPURenderArgs(args); /// Set render window setRenderWindow(args.renderWindow); /// Place source frame data in Metal texture source_container_ = std::make_unique<imetalling::Image2Texture>(_pMetalCmdQ, source_); /// Create empty target frame texture in Metal destination_container_ = std::make_unique<imetalling::Image2Texture>(_pMetalCmdQ, destination_); /// Get parameters for packing data in the memory area of the target frame OFX::BitDepthEnum dstBitDepth = destination->getPixelDepth(); OFX::PixelComponentEnum dstComponents = destination->getPixelComponents(); /// and original OFX::BitDepthEnum srcBitDepth = source->getPixelDepth(); OFX::PixelComponentEnum srcComponents = source->getPixelComponents(); /// show a message to the host system that something went wrong /// and cancel rendering of the current frame if ((srcBitDepth != dstBitDepth) || (srcComponents != dstComponents)) { OFX::throwSuiteStatusException(kOfxStatErrValue); } /// set in the current processor context a pointer to the memory area of the target frame setDstImg(destination_.get_ofx_image()); } void Processor::processImagesMetal() { try { if (enabled_) FalseColorKernel(_pMetalCmdQ, source_container_->get_texture(), destination_container_->get_texture()).process(); else PassKernel(_pMetalCmdQ, source_container_->get_texture(), destination_container_->get_texture()).process(); ImageFromTexture(_pMetalCmdQ, destination_, destination_container_->get_texture(), wait_command_queue_); } catch (std::exception &e) { interaction_->sendMessage(OFX::Message::eMessageError, "#message0", e.what()); } }
Để xây dựng dự án, bạn sẽ cần CMake và ít nhất nó phải là phiên bản 3.15. Ngoài ra, bạn sẽ yêu cầu Qt5.13, hỗ trợ việc lắp ráp gói dễ dàng và thuận tiện với trình cài đặt plugin trong thư mục hệ thống. Để bắt đầu cmake, trước tiên bạn phải tạo thư mục bản dựng.
Sau khi tạo thư mục build, bạn có thể thực hiện lệnh sau:
cmake -DPRINT_DEBUG=ON -DQT_INSTALLER_PREFIX=/Users/<user>/Develop/QtInstaller -DCMAKE_PREFIX_PATH=/Users/<user>/Develop/Qt/5.13.0/clang_64/lib/cmake -DPLUGIN_INSTALLER_DIR=/Users/<user>/Desktop -DCMAKE_INSTALL_PREFIX=/Library/OFX/Plugins .. && make install
Sau đó, trình cài đặt có tên IMFalseColorOfxInstaller.app , sẽ xuất hiện trong thư mục mà bạn đã chỉ định trong tham số PLUGIN_INSTALLER_DIR . Hãy tiếp tục và khởi động nó! Sau khi cài đặt thành công, bạn có thể khởi động DaVinci Resolve và bắt đầu sử dụng plugin mới của chúng tôi.
Bạn có thể tìm và chọn nó trong bảng OpenFX trên trang sửa màu và thêm nó làm nút.
liện kết ngoại