iOS Cocoa Touch vImage Subsampling

611 views Asked by At

I am working with vImages in Cocoa Touch, which in my case are basically ARGB-float-Arrays, and I need to do a subsampling. Low pass filtering is no problem using the vImage functions but how do I select one out 2x2 pixels (assuming I want to subsample by factor 2)? Of course I could use a vDSP stride function, but this only works for horizontal subsampling, not vertical subsampling.

I hope the following will clarify what I intend to do. I wish to select all pixels marked with an X as shown in this image:

X O X O X O
O O O O O O
X O X O X O
O O O O O O
X O X O X O
O O O O O O

But since the memory is linear, my array looks like this:

X O X O X O O O O O O O X O X O X O O O O O O O X O X O X O O O O O O O

How can I perform subsampling in a reasonable fashion?

EDIT: I am looking for an efficient way to do a two dimensional downsampling of a given bitmap stored as a one dimensional float-array (that includes negative values).

4

There are 4 answers

0
Stephen Canon On BEST ANSWER

The reality is that when you're doing this sort of subsampling, there isn't really anything clever that can be done; strided memory access doesn't admit many tricks to go fast, so any library code you use will be essentially equivalent to the C code you might write yourself in a few minutes. I would use a simple C implementation.

I believe that this can be done reasonably efficiently on the GPU, but that won't be a performance win unless your data is already in a GPU context. If you have to transfer the data there first, that cost will swamp any performance savings in the actual operation.

0
Ian Ollmann On

I realize that this is mostly just a "how to use pointers" question. However, the operation you are planning to do is not going to result in a high quality image. It will look pixelated and some details will be magnified and others lost.

Fortunately, image resampling techniques are a well researched area, and there are a lot of options here.

If you are on the GPU, the operation you describe is simply the nearest sampling method in OpenCL or OpenGL, so if you are there, you can just calculate the coordinate you want and pick nearest. The linear sampling method will probably look better.

If you are on the CPU, then you have some options. If the data is held in a CGImageRef, you can render it into a CGBitmapContextRef with scaling to reduce the size of the image by a factor of two in each dimension. I believe this gives you linear resampling, but that detail should be available somewhere.

If the data is sitting in a C array, then vImageScale_ARGBFFFF() is your friend. Just create a new buffer 1/2 the height and 1/2 the width of the original and use that function to downsample it to the right values. This will use Lanczos resampling which is a bit slower but looks better. (It is probably more appropriate for floating-point data, which I assumed you picked because you wanted high fidelity.) The Lanczos kernel has the advantage of combining low pass filtering and downsampling into a single pass, so there is less to do.

6
David H On

I do this all the time - you need to know the image width. So you have a pointer and you take every other pixel for one row, then you bump the pointer a whole row, then start taking every other pixel til you get to the end of the row, then bump the pointer one whole row again.

For me, I normally have the image rendered into a bit map context, so I know the number of bytes per pixel, the width in pixesl, and the bytes per row (to bump the pointer, but for you it may just be width x bytes-per-pixel).

EDIT: Sorry I was not clear. Given a bitmap of x columns and y rows, you create a new CGBitMapContext, and by using the above sample to take the "every other" pixel and write it to this new context. Now you have a second bitmap if exactly the pixels you say you want in your question. With that bitmap you can now apply whatever further processing you want.

0
onegray On

Since the memory is linear, lets interpret the source matrix as matrix of double width and half height:

X O X O X O  O O O O O O
X O X O X O  O O O O O O
X O X O X O  O O O O O O

After copying with interleaving (stride = 2) we have:

X X X  O O O
X X X  O O O
X X X  O O O

Then use vDSP_mmov to copy left side submatrix to the result submatrix.

For the copying with interleaving (first phase) can be used vDSP_zvmov function, but it is not optimized for floats. Probably vDSP_vsadd with 0 as scalar param will work faster.