Search code examples
iosobjective-cuiimageuigraphicscontextheightmap

Erase UIImage by lightness?


I have a height map (a grayscale image, with black as the lowest and white as the highest areas), and I'm trying to isolate everything above or below a certain threshold. My goal is to be able to take any height map, and return a new UIImage where everything below a certain height (i.e. darker than a given color) is copied, but anything above (i.e. lighter) is simply transparent.

enter image description here

Now I'm very new to this, so if there's a better way to go about this I'd love to hear it, but my first instinct is to create a new UIImage with the same frame, and then draw into it based on the lightness of a given area in the original. Something like...

-(UIImage*)mapIsolatedFromThreshold:(CGFloat)threshold showAbove:(BOOL)above;

Thus, the threshold variable could be 0.7 or 0.2 or whatever, and then you could set above to YES if you want everything higher (i.e. lighter) than the threshold, or NO if you want everything lower (i.e. darker).

The thing is, I've never done image processing or drawing before, so I don't know how to set up that method. I have a vague sense that it will involve UIGraphicsBeginImageContext and such, but I haven't been able to find any questions or tutorials that use it based on the lightness of another image.

How would I go about making a method like that? Or is there some pre-existing system for handling height maps in iOS, that makes this all a lot easier than I'm treating it?


Solution

  • After several hours of hunting around, I was able to piece together this method. And in case anyone else needed to do something similar, here it is. I still don't fully understand what it's doing, step by step, but the end result is what I wanted.

    -(UIImage*)imageWithLumaMaskFromDark:(CGFloat)lumaFloor toLight:(CGFloat)lumaCeil forImage:(UIImage*)image {
        CGImageRef rawImageRef = image.CGImage;
    
        const CGFloat colorMasking[6] = {lumaFloor, lumaCeil, lumaFloor, lumaCeil, lumaFloor, lumaCeil};
    
        UIGraphicsBeginImageContext(image.size);
        CGImageRef maskedImageRef = CGImageCreateWithMaskingColors(rawImageRef, colorMasking);
        {
            //if in iphone
            CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height);
            CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
        }
    
        CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef);
        UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
        CGImageRelease(maskedImageRef);
        UIGraphicsEndImageContext();
        return result;
    }
    

    The lumaFloor and lumaCeil inputs take values between 0 and 255, and everything outside that range becomes fully transparent.