1

I am doing some realtime image analysis on a live videostream. I am using vImage to calculate histograms and vDSP for some further processing. I have Objective-C code that has been working well over the years. I am now about to convert it to Swift. And while it works it is too slow. I have found that the main problem is converting the vImage histogram, which is UInt (vImagePixelCount), to Float that vDSP can handle. In Objective-C I am using vDSP to do the conversion:

  err = vImageHistogramCalculation_Planar8(&vBuffY,histogramY, 0);
  vDSP_vfltu32((const unsigned int*)histogramY,2,histFloatY,1,256);

However, the vImage histogram is UInt, not UInt32, so I can't use vDSP_vfltu32 in Swift. Instead I am using

  let err = vImageHistogramCalculation_Planar8(&vBuffY, &histogramY, 0)
  let histFloatY = histogramY.compactMap{ Float($0) }

The problem is that this code is more than 100 times slower than the objective-C version. Are there any alternatives that are faster?

Sten
  • 3,624
  • 1
  • 27
  • 26
  • Why don't you use the swift version? https://developer.apple.com/documentation/accelerate/1450255-vdsp_vfltu32 – Andreas Oetjen Oct 09 '20 at 09:06
  • Try `vDSP_vfltu32(histogramY, 2, &histFloatY, 1, 256)` where `histFloatY` is a `var`. – Sweeper Oct 09 '20 at 09:11
  • As mentioned in the question does vDSP_vfltu32 want UInt32 not UInt. Converting to UInt32 first is equally slow. – Sten Oct 09 '20 at 09:58

1 Answers1

3

vImageHistogramCalculation_Planar8() writes the histogram into a buffer with 256 elements of type vImagePixelCount which is a type alias for unsigned long in C, and that is a 64-bit integer on 64-bit platforms.

Your Objective-C code “cheats” by casting the unsigned long pointer to an unsigned int pointer in the call to vDSP_vfltu32 () and setting the stride to 2. So what happens here is that the lower 32-bit of each unsigned long are converted to a float. That works as long as the counts do not exceed the value 232-1.

You can do exactly the same in Swift, only that the type casting is here done by “rebinding” the memory:

let err = vImageHistogramCalculation_Planar8(&vBuffY, &histogramY, 0)
histogramY.withUnsafeBytes {
    let uint32ptr = $0.bindMemory(to: UInt32.self)
    vDSP_vfltu32(uint32ptr.baseAddress!, 2, &histFloatY, 1, 256);
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks, this might be a step in the right direction. But "withMemoryRebound" only works if the parameters have the same memory size (there is a note in the documentation). So the code crashes with a "fatal error" on 64 bit devices. – Sten Oct 10 '20 at 18:15
  • @Sten: You are right (that constraint is new or was not enforced in previous Swift versions, so I missed that). I have updated the code to use bindMemory, please check again. – Martin R Oct 10 '20 at 18:36
  • Thanks! it works fine and with the same performance as the Objective-c code. It has the same 2^32 limitation as the original code, but that is absolutely no problem in my case. – Sten Oct 11 '20 at 14:47