OFX, diğer adıyla OFX Görüntü İşleme API'si , 2D görsel efektler ve video birleştirme oluşturmaya yönelik açık bir standarttır. Eklenti benzeri bir uygulama geliştirme modelinde çalışır. Temel olarak, hem bir Ana Bilgisayar (bir dizi yöntem sağlayan bir uygulama) hem de bir Eklenti (bu seti uygulayan bir uygulama veya modül) olarak hizmet eder.
Bu yapılandırma, ana bilgisayar uygulamasının işlevselliğinin sınırsız genişletilmesi potansiyelini sunar.
Final Cut X ve DaVinci Resolve Studio gibi uygulamalar, sürüm 16'dan başlayarak, Apple Metal işlem hatlarını tamamen destekler. OpenCL ve Cuda'ya benzer şekilde, OFX durumunda, platforma özgü bir komut kuyruğunun tanımlayıcısını veya işleyicisini elde edebilirsiniz. Ana bilgisayar sistemi ayrıca bu tür kuyruklardan oluşan bir havuz tahsis etme ve bunlar üzerindeki hesaplamaları dengeleme sorumluluğunu da üstlenir.
Ayrıca kaynak ve hedef görüntü klibi verilerini GPU belleğine yerleştirerek genişletilebilir işlevselliğin geliştirilmesini önemli ölçüde basitleştirir.
Resolve ile işler biraz daha karmaşık. DaVinci, bazı sınırlamalara rağmen OFX v1.4 desteğini duyurdu. Özellikle arayüz işlevleriyle çalışmaya yönelik bazı yöntemler kullanılamaz. Hangi yöntemin kullanılabilir olduğunu belirlemek için OFX, desteklenen paketi anahtar/değer sorguları aracılığıyla incelemenize olanak tanır.
Eklenti kodundaki yayınlama yöntemleri C çağrılarına dayanmaktadır. Ancak biz C++17 için uyarlanmış OpenFXS C++ kabuğunu kullanacağız. Kolaylık sağlamak için her şeyi tek bir depoda derledim: açık kaynaklı Dehancer projesinden alınan dehancer-external .
Bu projede, orijinal olarak Bruno Nicoletti tarafından yazılan ve zamanla ticari ve açık kaynaklı video işleme projelerinde popüler hale gelen, OpenFX'in C++ uzantısı olan OpenFXS'i kullanacağım.
Orijinal OpenFXS, modern C++ lehçelerine uyarlanmamıştı, bu yüzden onu C++17 ile uyumlu hale getirecek şekilde güncelledim.
OFX ve dolayısıyla OFXS, ana program tarafından dinamik olarak yüklenen bağımsız bir yazılım modülüdür. Esas itibariyle ana uygulama başlatıldığında yüklenen dinamik bir kütüphanedir. OpenFXS, OFX gibi, yöntem imzalarını yayınlamalıdır. Bu nedenle koddaki bir C yöntemini kullanıyoruz.
OpenFXS'te geliştirmeye başlamak için uygulamanızda yeni işlevler oluşturmak amacıyla kullanılan birkaç ortak sınıf kümesini kabul etmeniz gerekir. Genellikle yeni bir projede bu sınıflardan miras almanız ve bazı sanal yöntemleri uygulamanız veya geçersiz kılmanız gerekir.
Ana sistemde kendi eklentinizi oluşturmak için aşağıdaki genel sınıfları ve aynı yöntemi öğrenerek başlayalım:
Video çekme işlemini basitçe bir fotoğraftaki görüntüyü yakalamaktan ayıran özelliklerden biri, sahnelerin dinamik değişimi ve hem bir bütün olarak sahnelerin hem de görüntüdeki alanların aydınlatılmasıdır. Bu, çekim işlemi sırasında pozlamanın nasıl kontrol edileceğini belirler.
Dijital videoda, alanların maruz kalma düzeyinin, her biri kendi rengiyle renklendirilmiş sınırlı sayıda bölgeye eşlendiği, operatörler için bir kontrol izleme modu vardır.
Bu moda bazen "yırtıcı" veya Yanlış Renk modu denir. Ölçekler genellikle IRE ölçeğine atıfta bulunur.
Böyle bir monitör, kamera çekim parametrelerini ayarlarken pozlama bölgelerini görmenize ve önemli hatalardan kaçınmanıza olanak tanır. Fotoğrafta pozlama sırasında anlam olarak benzer bir şey kullanılır - örneğin Adams'a göre imar.
Belirli bir hedefi bir pozlama ölçerle ölçebilir ve hangi bölgede bulunduğunu görebilirsiniz; gerçek zamanlı olarak algılama kolaylığı için düzgün bir şekilde renklendirilmiş bölgeleri görebiliriz.
Bölgelerin sayısı kontrol monitörünün hedefleri ve yeteneklerine göre belirlenir. Örneğin, Arri Alexa kameralarıyla kullanılan bir monitörde 6'ya kadar bölge bulunabilir.
Örneğe geçmeden önce, OpenFXS'i Metal dokular gibi kaynak verileri işlemek için bir platform olarak uygulamak için bazı basit proxy sınıfları eklememiz gerekiyor. Bu sınıflar şunları içerir:
imetalling::Image2Texture : Verileri klip arabelleğinden Metal dokuya aktarmak için kullanılan bir işlev. DaVinci'den, herhangi bir yapının arabelleğini ve görüntü kanalı değerlerinin paketlenmesini eklentiye çıkarabilirsiniz; bu, benzer bir biçimde döndürülmelidir.
OFX'te akış formatıyla çalışmayı kolaylaştırmak için ana bilgisayardan belirli bir türdeki verileri önceden hazırlamasını isteyebilirsiniz. RGBA - kırmızı/yeşil/mavi/alfa olarak paketlenmiş şamandıraları kullanacağım.
OFXS temel sınıflarını miras alıyoruz ve Metal çekirdeğin nasıl çalıştığına dair ayrıntılara girmeden işlevselliğimizi yazıyoruz:
Ek olarak, MSL'de ana bilgisayar kodu ile çekirdek kodunu mantıksal olarak ayırmak için Metal üzerine inşa edilmiş birkaç yardımcı program sınıfına ihtiyacımız olacak. Bunlar şunları içerir:
imetalling::FalseColorKernel : Ana işlevsel sınıfımız, belirli sayıda renge poster oluşturan (alt örnekler) bir "yırtıcı" öykünücüsü.
"Yırtıcı" modunun çekirdek kodu şöyle görünebilir:
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 Eklentisinin Başlatılması
imetalling::falsecolor::Factory.
Bu sınıfta tek bir parametre belirleyeceğiz - monitörün durumu (açık veya kapalı). Örneğimiz için bu gereklidir.
OFX::PluginFactoryHelper
miras alacağız ve beş yöntemi aşırı yükleyeceğiz:
ImageEffectDescriptor
koduna bakın.
defineInContext(ImageEffectDescriptor&,ContextEnum) : describe
yöntemine benzer şekilde bu yöntem de eklenti yüklendiğinde çağrılır ve sınıfımızda tanımlanması gerekir. Geçerli bağlamla ilişkili özellikleri tanımlamalıdır.
Bağlam, bir klipteki filtre, boyama, geçiş efekti veya kare yeniden zamanlayıcı gibi uygulamanın birlikte çalıştığı işlem türlerini belirler.
ImageEffect
türündeki bir nesneye bir işaretçi döndürürüz. Başka bir deyişle, hem ana bilgisayar programındaki kullanıcı etkinliklerine hem de kaynak çerçeveyi hedef çerçeveye dönüştürmeye (dönüştürmeye) ilişkin tüm işlevleri tanımladığımız imetalling::falsecolor::Plugin
: OFX::ImageEffect *Factory::createInstance(OfxImageEffectHandle handle,OFX::ContextEnum) { return new Plugin(handle); }
Bu aşamada, OFX modülü ile bir paket derlerseniz, eklenti ana bilgisayar uygulamasında zaten mevcut olacaktır ve DaVinci'de düzeltme düğümüne yüklenebilecektir.
Ancak bir eklenti örneğiyle tam olarak çalışmak için en azından etkileşimli kısmı ve gelen video akışının işlenmesiyle ilişkili kısmı tanımlamanız gerekir.
Bunu yapmak için OFX::ImageEffect sınıfından miras alıyoruz ve sanal yöntemleri aşırı yüklüyoruz:
changeParam(const OFX::InstanceChangedArgs&, const std::string&) - Bu yöntem, olayı işleme mantığını tanımlamamıza olanak tanır. Olay türü, OFX::InstanceChangedArgs::reason değerine göre belirlenir ve şunlar olabilir: eChangeUserEdit, eChangePluginEdit, eChangeTime - bir özelliğin kullanıcı tarafından düzenlenmesi, bir eklenti veya ana bilgisayar uygulamasında değiştirilmesi sonucu meydana gelen olay veya zaman çizelgesindeki bir değişikliğin sonucu olarak.
İkinci parametre, eklenti başlatma aşamasında tanımladığımız dize adını belirtir; bizim durumumuzda bu bir parametredir: false_color_enabled_check_box .
OFX ile etkileşimli etkileşimin uygulanmasını Interaction.cpp kodunda okuyabilirsiniz. Gördüğünüz gibi kliplere işaretçiler alıyoruz: kaynak olan ve hedef dönüşümü koyacağımız hafıza alanı.
Dönüşümü başlatmanın tüm mantığını tanımlayacağımız başka bir mantıksal katman ekleyeceğiz. Bizim durumumuzda şu ana kadar geçersiz kılmanın tek yöntemi budur:
Başlatma aşamasında, kullanışlı özelliklere sahip bir nesne bizim için kullanılabilir hale geldi: video akışına yönelik en azından bir işaretçimiz (daha doğrusu, çerçeve görüntü verilerini içeren bir hafıza alanı) ve en önemlisi bir Metal komutları kuyruğumuz var.
Artık bizi çekirdek kodunu yeniden kullanmanın basit bir biçimine yaklaştıracak genel bir sınıf oluşturabiliriz. OpenFXS uzantısının zaten böyle bir sınıfı var: OFX::ImageProcessor; sadece onu aşırı yüklememiz gerekiyor.
Yapıcıda OFX::ImageEffect parametresi vardır, yani içinde yalnızca eklenti parametrelerinin mevcut durumunu değil aynı zamanda GPU ile çalışmak için gerekli olan her şeyi alacağız.
Bu aşamada sadece prosesImagesMetal() metodunu aşırı yüklememiz ve Metal üzerinde hali hazırda uygulanan kernellerin işlenmesini başlatmamız gerekiyor.
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()); } }
Projeyi oluşturmak için CMake'e ihtiyacınız olacak ve en az 3.15 sürümü olmalıdır. Ek olarak, paketin sistem dizinindeki eklenti yükleyicisiyle kolay ve rahat bir şekilde birleştirilmesine yardımcı olan Qt5.13'e ihtiyacınız olacak. Cmake'yi başlatmak için önce bir derleme dizini oluşturmanız gerekir.
Derleme dizinini oluşturduktan sonra aşağıdaki komutu çalıştırabilirsiniz:
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
Daha sonra PLUGIN_INSTALLER_DIR parametresinde belirttiğiniz dizinde IMFalseColorOfxInstaller.app isimli kurulum dosyası karşınıza çıkacaktır. Hadi devam edelim ve başlatalım! Kurulum başarılı olduktan sonra DaVinci Resolve'yi başlatabilir ve yeni eklentimizi kullanmaya başlayabilirsiniz.
Renk düzeltme sayfasındaki OpenFX panelinde bulup seçebilir ve düğüm olarak ekleyebilirsiniz.
Dış bağlantılar