paint-brush
나만의 Apple Metal 플러그인을 개발하고 DaVinci Resolve에 통합하는 쉬운 방법~에 의해@denissvinarchuk
3,610 판독값
3,610 판독값

나만의 Apple Metal 플러그인을 개발하고 DaVinci Resolve에 통합하는 쉬운 방법

~에 의해 Denis Svinarchuk13m2024/03/13
Read on Terminal Reader

너무 오래; 읽다

OFX 이미지 처리 API라고도 알려진 OFX는 2D 시각 효과 및 비디오 합성을 생성하기 위한 개방형 표준입니다. 플러그인과 유사한 애플리케이션 개발 모델에서 작동합니다. 기본적으로 호스트(메서드 세트를 제공하는 애플리케이션)와 플러그인(이 세트를 구현하는 애플리케이션 또는 모듈) 역할을 모두 수행합니다. 이 구성은 호스트 응용 프로그램 기능을 무제한으로 확장할 수 있는 가능성을 제공합니다.
featured image - 나만의 Apple Metal 플러그인을 개발하고 DaVinci Resolve에 통합하는 쉬운 방법
Denis Svinarchuk HackerNoon profile picture

OFX 이미지 처리 API 라고도 알려진 OFX는 2D 시각 효과 및 비디오 합성을 생성하기 위한 개방형 표준입니다. 플러그인과 유사한 애플리케이션 개발 모델에서 작동합니다. 기본적으로 호스트(메서드 세트를 제공하는 애플리케이션)와 플러그인(이 세트를 구현하는 애플리케이션 또는 모듈) 역할을 모두 수행합니다.


이 구성은 호스트 응용 프로그램 기능을 무제한으로 확장할 수 있는 가능성을 제공합니다.

DaVinci Resolve와 Metal

버전 16부터 Final Cut X 및 DaVinci Resolve Studio와 같은 애플리케이션은 Apple Metal 파이프라인을 완벽하게 지원합니다. OpenCL 및 Cuda와 유사하게 OFX의 경우 플랫폼별 명령 대기열의 설명자 또는 처리기를 얻을 수 있습니다. 또한 호스트 시스템은 이러한 대기열 풀을 할당하고 이에 대한 계산 균형을 조정하는 역할을 담당합니다.


또한 소스 및 대상 이미지 클립 데이터를 GPU 메모리에 배치하여 확장 가능한 기능 개발을 크게 단순화합니다.

Resolve의 OFX 버전 지원

Resolve를 사용하면 상황이 약간 더 복잡해집니다. DaVinci는 일부 제한 사항이 있지만 OFX v1.4에 대한 지원을 발표했습니다. 특히, 인터페이스 기능 작업을 위한 일부 방법을 사용할 수 없습니다. 어떤 방법을 사용할 수 있는지 확인하기 위해 OFX를 사용하면 키/값 쿼리를 통해 지원되는 제품군을 검사할 수 있습니다.


플러그인 코드의 게시 방법은 C 호출을 기반으로 합니다. 하지만 우리는 C++17에 맞춰진 OpenFXS C++ 셸을 사용할 것입니다. 편의상 모든 것을 하나의 저장소로 컴파일했습니다: dehancer-external 오픈 소스 Dehancer 프로젝트 에서 가져온 것입니다.

OFXS 개념

이 프로젝트에서는 원래 Bruno Nicoletti 가 작성했으며 시간이 지나면서 상업 및 오픈 소스 비디오 처리 프로젝트에서 인기를 얻은 OpenFX의 C++ 확장인 OpenFXS를 사용할 것입니다.


원래 OpenFXS는 최신 C++ 언어에 적합하지 않았기 때문에 C++17 과 호환되도록 업데이트했습니다.


OFX, 즉 OFXS는 호스트 프로그램에 의해 동적으로 로드되는 독립형 소프트웨어 모듈입니다. 기본적으로 이는 기본 애플리케이션이 시작될 때 로드되는 동적 라이브러리입니다. OFX와 마찬가지로 OpenFXS는 메서드 서명을 게시해야 합니다. 따라서 우리는 코드에서 하나의 C 메서드를 사용합니다.


OpenFXS에서 개발을 시작하려면 애플리케이션에서 새로운 기능을 생성하는 데 사용되는 몇 가지 공통 클래스 세트에 동의해야 합니다. 일반적으로 새 프로젝트에서는 이러한 클래스를 상속하고 일부 가상 메서드를 구현하거나 재정의해야 합니다.


호스트 시스템에서 자신만의 플러그인을 만들려면 먼저 다음 공개 클래스와 동일한 방법을 숙지해 보겠습니다.


  • OFX::PluginFactoryHelper는 플러그인의 데이터 구조 모음과 제어판을 생성하기 위한 기본 템플릿입니다(비어 있을 수 있음). 상속된 클래스는 개발자가 자신의 모듈을 등록하는 호스트 시스템에 매개변수 및 사전 설정 세트를 등록하는 싱글톤 객체를 생성합니다.


  • OFX::ParamSetDescriptor - 구조 속성을 생성하고 저장하기 위한 기본 컨테이너 클래스입니다.


  • OFX::ImageEffectDescriptor - 데이터 처리 프로시저를 호출할 때 그래픽 데이터를 조작할 때 사용되는 속성의 컨테이너입니다. 처리 매개변수의 컨텍스트를 내부 데이터베이스에 저장하고 각 인스턴스에 대해 정의된 플러그인 속성을 사용하기 위해 호스트 애플리케이션에서 사용됩니다.


  • OFX::ParamSet - 등록된 데이터 구조를 조작할 수 있는 설정 세트입니다.


  • OFX::ImageEffect - OFX::ParamSet에서 상속된 그래픽 데이터에 대한 효과 설정 세트입니다.


  • OFX::MultiThread::Processor - 하위 클래스에서는 데이터 스트림 처리(이미지 또는 비디오)를 구현해야 합니다.


  • OFX::Plugin::getPluginIDs - 호스트 애플리케이션에 플러그인(팩토리)을 등록하는 방법입니다.

거짓 컬러

비디오 촬영 과정과 단순히 사진 속 이미지를 캡처하는 과정을 구별하는 한 가지 특징은 장면의 역동적인 변화와 두 장면 전체 및 이미지 내 영역의 조명입니다. 이는 촬영 과정에서 노출이 제어되는 방식을 결정합니다.


디지털 비디오에는 영역의 노출 수준이 제한된 영역 세트로 매핑되고 각각 고유한 색상으로 착색되는 운영자를 위한 제어 모니터 모드가 있습니다.


이 모드는 "프레데터" 또는 가색상 모드라고도 합니다. 척도는 일반적으로 IRE 척도를 참조합니다.


이러한 모니터를 사용하면 노출 영역을 확인하고 카메라 촬영 매개변수를 설정할 때 심각한 실수를 피할 수 있습니다. 예를 들어 Adams에 따른 구역 지정과 같이 사진에 노출할 때 비슷한 의미가 사용됩니다.


노출계를 사용하여 특정 대상을 측정하고 해당 대상이 어느 구역에 있는지 확인할 수 있으며 실시간으로 인식하기 쉽도록 깔끔하게 착색된 구역을 볼 수 있습니다.


영역 수는 제어 모니터의 목표와 기능에 따라 결정됩니다. 예를 들어 Arri Alexa 카메라와 함께 사용되는 모니터는 최대 6개 구역을 통합할 수 있습니다.


16개 영역이 있는 소프트웨어 "프레데터" 버전


확장 추가

예제를 진행하기 전에 OpenFXS를 Metal 텍스처와 같은 소스 데이터 처리용 플랫폼으로 구현하기 위해 몇 가지 간단한 프록시 클래스를 추가해야 합니다. 이러한 수업에는 다음이 포함됩니다.


  • imetalling::Image : OFX 클립 데이터용 프록시 클래스입니다.


  • imetalling::Image2Texture : 클립 버퍼의 데이터를 Metal 텍스처로 전송하기 위한 기능자입니다. DaVinci에서는 이미지 채널 값의 모든 구조와 패키징의 버퍼를 플러그인으로 추출할 수 있으며 유사한 형식으로 반환되어야 합니다.


    OFX에서 스트림 형식 작업을 더 쉽게 하기 위해 호스트에 특정 유형의 데이터를 미리 준비하도록 요청할 수 있습니다. RGBA(빨간색/녹색/파란색/알파)로 포장된 부동 소수점을 사용하겠습니다.


  • imetalling::ImageFromTexture : 스트림을 호스트 시스템 버퍼로 변환하기 위한 역방향 함수입니다. 보시다시피 Metal 컴퓨팅 코어가 텍스처가 아닌 버퍼와 직접 작동하도록 가르치는 경우 계산을 크게 최적화할 수 있는 가능성이 있습니다.


OFXS 기본 클래스를 상속하고 Metal 코어 작동 방식을 자세히 설명하지 않고 기능을 작성합니다.


  • imetalling::falsecolor::Processor : 여기서는 스트림 변환을 구현하고 처리를 시작합니다.


  • imetalling::falsecolor::Factory : 이는 플러그인에 대한 제품군 설명의 특정 부분이 됩니다. 우리는 구조 설정과 관련된 몇 가지 필수 호출을 구현하고 특정 기능을 갖춘 OFX::ImageEffect 클래스의 인스턴스를 생성해야 합니다. 이 클래스는 구현에서 Interaction 및 Plugin이라는 두 개의 하위 클래스로 나뉩니다.


  • imetalling::falsecolor::Interaction : 효과 작업의 대화형 부분 구현. 본질적으로 이는 플러그인 매개변수의 변경 사항 처리와 관련된 OFX::ImageEffect의 가상 메서드만 구현한 것입니다.


  • imetalling::falsecolor::Plugin : 스레드 렌더링 구현, 즉 imetalling::Processor 실행.


또한 MSL에서 호스트 코드와 커널 코드를 논리적으로 분리하려면 Metal 위에 구축된 여러 유틸리티 클래스가 필요합니다. 여기에는 다음이 포함됩니다.


  • imetalling::Function : Metal 명령 대기열 작업을 모호하게 하는 기본 클래스입니다. 주요 매개변수는 MSL 코드의 커널 이름과 커널 호출 실행자입니다.


  • imetalling:Kernel : 소스 텍스처를 대상 텍스처로 변환하는 일반 클래스로, Function을 확장하여 MSL 커널 호출을 위한 매개변수를 간단히 설정합니다.


  • imetalling::PassKernel : 커널을 우회합니다.


  • imetalling::FalseColorKernel : 지정된 색상 수로 포스터화(다운샘플링)하는 "프레데터" 에뮬레이터인 주요 기능 클래스입니다.


"predator" 모드의 커널 코드는 다음과 같습니다:

 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); }


OFX 플러그인 초기화

imetalling::falsecolor::Factory. 이 수업에서는 모니터 상태(켜짐 또는 꺼짐)라는 단일 매개변수를 설정합니다. 이는 우리의 예에 필요합니다.

OFX::PluginFactoryHelper 상속하고 다섯 가지 메서드를 오버로드합니다.


  • load() : 이 메소드는 플러그인이 처음 로드될 때 인스턴스를 전역적으로 구성하기 위해 호출됩니다. 이 메서드를 오버로드하는 것은 선택 사항입니다.


  • unload() : 이 메서드는 예를 들어 메모리를 지우기 위해 인스턴스가 언로드될 때 호출됩니다. 이 메서드를 오버로드하는 것도 선택 사항입니다.


  • explain(ImageEffectDescriptor&) : 플러그인이 로드될 때 OFX 호스트가 호출하는 두 번째 메서드입니다. 이는 가상이므로 클래스에서 정의해야 합니다. 이 방법에서는 컨텍스트 유형에 관계없이 플러그인의 모든 속성을 설정해야 합니다. 속성에 대한 자세한 내용은 ImageEffectDescriptor 코드를 참조하세요.


  • explainInContext(ImageEffectDescriptor&,ContextEnum) : describe 메소드와 유사하게 이 메소드는 플러그인이 로드될 때 호출되며 클래스에서 정의되어야 합니다. 현재 컨텍스트와 관련된 속성을 정의해야 합니다.


    컨텍스트에 따라 필터, 페인트, 전환 효과 또는 클립의 프레임 리타이머와 같이 응용 프로그램이 작동하는 작업 유형이 결정됩니다.


  • createInstance(OfxImageEffectHandle, ContextEnum) : 이것은 우리가 오버로드하는 가장 중요한 방법입니다. ImageEffect 유형의 객체에 대한 포인터를 반환합니다. 즉, 호스트 프로그램의 사용자 이벤트와 소스 프레임을 대상 프레임으로 렌더링(변환)하는 것과 관련된 모든 기능을 정의한 imetalling::falsecolor::Plugin 다음과 같습니다.
 OFX::ImageEffect *Factory::createInstance(OfxImageEffectHandle handle,OFX::ContextEnum) {     return new Plugin(handle);   }


이벤트 처리

이 단계에서 OFX 모듈로 번들을 컴파일하면 호스트 애플리케이션에서 플러그인을 이미 사용할 수 있으며 DaVinci에서는 수정 노드에 로드할 수 있습니다.


그러나 플러그인 인스턴스를 완벽하게 사용하려면 최소한 대화형 부분과 수신 비디오 스트림 처리와 관련된 부분을 정의해야 합니다.


이를 위해 OFX::ImageEffect 클래스를 상속하고 가상 메서드를 오버로드합니다.


  • ChangeParam(const OFX::InstanceChangedArgs&, const std::string&) - 이 메서드를 사용하면 이벤트 처리를 위한 논리를 정의할 수 있습니다. 이벤트 유형은 OFX::InstanceChangedArgs::reason 값에 의해 결정되며 eChangeUserEdit, eChangePluginEdit, eChangeTime일 수 있습니다. - 사용자가 속성을 편집하거나 플러그인 또는 호스트 애플리케이션에서 변경한 결과로 이벤트가 발생했습니다. 타임라인 변경으로 인해.


    두 번째 매개변수는 플러그인 초기화 단계에서 정의한 문자열 이름을 지정합니다. 우리의 경우 false_color_enabled_check_box 매개변수 중 하나입니다.


  • isIdentity(...) - 이 메서드를 사용하면 이벤트에 반응하기 위한 논리를 정의하고, 무언가 변경되었는지 여부와 렌더링이 적합한지 여부를 결정하는 상태를 반환할 수 있습니다. 메서드는 false 또는 true를 반환해야 합니다. 이는 불필요한 계산 수를 최적화하고 줄이는 방법입니다.


Interaction.cpp 코드에서 OFX와의 대화형 상호 작용 구현을 읽을 수 있습니다. 보시다시피, 우리는 클립에 대한 포인터(소스 클립과 대상 변환을 넣을 메모리 영역)를 받습니다.

렌더링 실행 구현

변환을 시작하기 위한 모든 논리를 정의할 또 다른 논리 계층을 추가하겠습니다. 우리의 경우 지금까지 재정의하는 유일한 방법은 다음과 같습니다.


  • render(const OFX::RenderArguments& args) - 여기에서 클립의 속성을 확인하고 렌더링 방법을 결정할 수 있습니다. 또한 이 단계에서는 Metal 명령 대기열과 현재 타임라인 속성과 관련된 일부 유용한 속성을 사용할 수 있게 됩니다.

처리

출시 단계에서 유용한 속성을 가진 개체를 사용할 수 있게 되었습니다. 최소한 비디오 스트림에 대한 포인터(보다 정확하게는 프레임 이미지 데이터가 있는 메모리 영역)와 가장 중요한 것은 Metal 명령 대기열이 있습니다.


이제 커널 코드를 재사용하는 간단한 형태에 더 가까워지는 일반 클래스를 구성할 수 있습니다. OpenFXS 확장에는 이미 OFX::ImageProcessor와 같은 클래스가 있습니다. 우리는 그것을 오버로드하면 됩니다.


생성자에는 OFX::ImageEffect 매개변수가 있습니다. 즉, 여기에서 플러그인 매개변수의 현재 상태뿐만 아니라 GPU 작업에 필요한 모든 것을 수신하게 됩니다.


이 단계에서는 processImagesMetal() 메서드를 오버로드하고 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());      }    }


프로젝트 구축

프로젝트를 빌드하려면 CMake가 필요하며 버전 3.15 이상이어야 합니다. 또한 시스템 디렉토리의 플러그인 설치 프로그램을 사용하여 번들을 쉽고 편리하게 조립하는 데 도움이 되는 Qt5.13이 필요합니다. cmake를 시작하려면 먼저 빌드 디렉터리를 만들어야 합니다.


빌드 디렉터리를 생성한 후 다음 명령을 실행할 수 있습니다.


 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 


당신의 개인적인 "포식자"


그러면 IMFalseColorOfxInstaller.app 이라는 설치 프로그램이 PLUGIN_INSTALLER_DIR 매개변수에 지정한 디렉터리에 나타납니다. 이제 시작해 보세요! 설치가 성공적으로 완료되면 DaVinci Resolve를 시작하고 새로운 플러그인을 사용할 수 있습니다.


색상 보정 페이지의 OpenFX 패널에서 찾아 선택하고 노드로 추가할 수 있습니다.



가색 작업



외부 링크

  1. 거짓 컬러 OFX 플러그인 코드
  2. 오픈 이펙트 협회
  3. DaVinci Resolve 다운로드 - Resolve + 예제 아래 OFX 헤더 파일 버전 및 OFXS 라이브러리 코드