Problémy s rámem v průměru s Core Image

0

Otázka

Narážím na velmi nepříjemný problém v provedení jednoduchý rám v průměru postupu s Core Image. Stručně řečeno, jsem se urvat snímky z videa vyrovnávací paměti v zachycení výstupní metody:

func captureOutput(_ output: AVCaptureOutput, 
                     didOutput sampleBuffer: CMSampleBuffer, 
                     from connection: AVCaptureConnection) {
    
    guard let cvBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
                return
            }

    let newImage = CIImage(cvImageBuffer: cvBuffer)

    ...
   
    // Frame averaging part using a CIImageAccumulator
    if (slowIncrement == 0.0) {
                
        accumulator?.setImage(newImage)
                        
        } else {

          makeAverageFiler.currentStack = accumulator?.image()
          makeAverageFiler.newImage = newImage
          makeAverageFiler.count = slowIncrement
          guard let processedImage = makeAverageFiler.outputImage else {return}
                                                
          accumulator?.setImage(processedImage)
                        
          }
       
     slowIncrement += 1.0
     
     ...




} 

Udělal jsem vlastní filtr s následující jádra:

float4 makeAverage(sample_t currentStack, sample_t newImage, float stackCount) {
          
          float4 cstack = unpremultiply(currentStack);
          float4 nim = unpremultiply(newImage);
          float4 avg = ((cstack * stackCount) + nim) / (stackCount + 1.0);
          
          return premultiply(avg);
          
      }

Algoritmus by měl být správný. Když jsem test stejné s malým python fragment kódu na video snímků, funguje to perfektně. V app funguje také bod. Nicméně, když aplikace získává více a více snímků, vidím, že barvy se pomíchaly a divné barevné skvrny se začnou objevovat. Mám podezření, že základní obraz není provádění výpočtů na barevné kanály správně, nějak barevné kanály sejmou.

To je, jak jsem inicializován CIImageAccumulator:

let accumulator = CIImageAccumulator(extent: CGRect(x: 0, y: 0, width: 3024, height:  4032), format: .RGBAf)

Musím použít akumulátor, jinak, využití paměti roste na dobu neurčitou a aplikace přestane pracovat.

Vidím, že změna formátu má vliv na výsledky. Nicméně, nemohl jsem najít vhodný formát, který by tento problém zmizet.

Co dělám špatně? Na cvImageBuffer má 32 bit-per-pixel ARGB formát pixelu. Je jádro, obrazové jádro provádějící konverzi na 128 bit-per-pixel automaticky?

Další věcí, které jsem zkoušel:

  1. unmultiply a premultiply nezdá opravit výsledky
  2. změna workingformat z CIContext také nezdá do práce. Zejména, zdá se, že jsem se může nastavit pouze v sRGB formátu jako pracovní CIContextOption. Jiné formáty následek zvýšení výjimku.

Já bych opravdu rád, aby se zabránilo používání kovových vlastní shadery a držet core image. Díky předem za vaši pomoc!

Aktualizace

Zde je příklad podivné skvrny, které se začnou objevovat po získání chvíli. V tomto případě jsem jen pohybem telefonu kolem, zatímco zachycení. V reálném světě použití, tento problém se zdá být silně při získávání pomalu se pohybující mraky.

enter image description here

Aktualizace 2

Prohlašuji CIContext jako vlastnost view controller. Pak jsem ji inicializovat v viewDidLoad takto:

ciContext = CIContext(mtlDevice: metalView.metalDevice, options: [.workingFormat : CIFormat.RGBAf,
                                                                      .workingColorSpace: NSNull(),
                                                                      .cacheIntermediates : false,
                                                                      .highQualityDownsample: true])

Používám ciContext v několika místech, jak vykreslit CIImages na tažná, vytvořit přechodné CGImages a také uložit obrázky ve formátu Jpeg. Například, tady je render použití:

self.ciContext.render(centeredImage,
                          to: currentDrawable.texture,
                          commandBuffer: commandBuffer,
                          bounds: CGRect(origin: .zero, size: view.drawableSize),
                          colorSpace: CGColorSpaceCreateDeviceRGB())
avfoundation ciimage metal
2021-11-20 12:17:18
2
0

Po týdnech pokusů a omylů, jediné řešení, které jsem našel, byl opustit Core Image a pracovat čistě s Kovem. Jsem si téměř jistý, že Core Image se potýká s pixel formát konverze a nemohl jsem přijít na to, proč.

Tento postup problém vyřešil:

  • Převést pixel vyrovnávací paměti (od captureOutput) do MTLTexture pomocí CVMetalTextureCacheCreateTextureFromImage a CVMetalTextureGetTexture. V tomto případě, formát pixelů mezi pixel vyrovnávací paměti a kovové textury musí odpovídat. Tak jsem nastavit pixel formát kovové textury .rgba8Unorm. Ujistěte se, že pixel vyrovnávací paměti je v BGRA32, nastavení videoOutput videoFormats vlastnosti.

  • Skryté výše texturu .rgbaFloat32. Použil jsem vlastní jádro pro tento.

  • Vytvoření nové prázdné textury .rgbaFloat32 pixel formátu. Tato nová struktura se bude konat výsledků rám průměrování (foto stohování).

  • Napsat novou texturu pomocí vlastní jádro. V podstatě, jádro je přesně jako CIKernel v otázce, jen přepsat na Kov.

V mém případě, tento nový postup dal velmi pěkné výsledky, barvy jsou přirozenější a není výstřižek. Poznámka, bez konverze na float32, kód by stále nebude fungovat, a současné barvy výstřižek.

Tato implementace také přinesl následující výhody:

  1. Zvýšení výkonu. Vše se děje v reálném čase, žádné rámy jsou vynechány. Jádro Image realizace byla výrazně pomalejší.
  2. Není třeba pro akumulátor. Žádná magie tady, MTLTextures choval, jak se očekává, aniž by problémy s pamětí jako s CIImages.
  3. Méně paměti, méně CPU a méně energie potřebné. Během testování, telefon zůstal pěkně v pohodě. S core image, to bude vždy běžet horko.
2021-12-19 11:02:26
0

Výchozí CIContext pixel formát je RGBAh, což je o 64 bitů na pixel. Potřebujete RGBAf, protože pracujete s 128 bitů na pixel obrázků. Formát musí být výslovně uvedeno při vytváření CIContent, jako je tento:

let context = CIContext(options: [.workingFormat: CIFormat.RGBAf])

To by nastavit přesnost pro celý plynovod, který by popraven, když se obraz dostane vykreslen.

2021-12-20 19:15:49

Souhlasím s tím, že toto řešení by mělo fungovat a možná pořád dělám něco špatně. Nicméně, jen změna pracovní formátu CIFormat.RGBAf nezdá se, že vyřešit můj problém.
Salvo89

Jo, to je záhadný. I když vypadá to, že už mají alternativní řešení pomocí Kovu a textury, vadilo by vám, sdílení kódu, kde můžete nastavit formát RGBAf a jak budete používat, že CIContext?
Vadim Dagman

Jistě, mám aktualizováno na otázky, požadované fragmenty kódu
Salvo89

V jiných jazycích

Tato stránka je v jiných jazycích

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................