BradLarson / GPUImage

An open source iOS framework for GPU-based image and video processing
http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework
BSD 3-Clause "New" or "Revised" License
20.2k stars 4.61k forks source link

How to replace all pixels in an image which have R, G and B values over a certain value? #2588

Open Pranoy1c opened 5 years ago

Pranoy1c commented 5 years ago

Please bear with me as I am pretty new to iOS and GPUImage too. I am trying to figure out how to replace all pixels in an image which have R, G and B values over a certain value with another R,G,B value.

So let's say I need to replace all pixels which have an:

R value of over 20 with 100, 
G value of over 100 with 200,
B value of over 40 with 0

I am able to do this the old school way but it is very slow and I need to do it as fast as possible:

- (CGImageRef)processImageOldSchoolWay:(CGImageRef)image
{
    uint8_t *bufptr;

    int width  = (int)CGImageGetWidth( image );
    int height = (int)CGImageGetHeight( image );

    // allocate memory for pixels
    uint32_t *pixels = calloc( width * height, sizeof(uint32_t) );

    // create a context with RGBA pixels
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate( pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast );

    // draw the image into the context
    CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image );

    // manipulate the pixels
    bufptr = (uint8_t *)pixels;
    for (NSInteger y = 0; y < height; y++){
        for (NSInteger x = 0; x < width; x++ )
        {

            if (bufptr[3]>=(int)self.redslider.value) {
                bufptr[3]=100;
            }
            if (bufptr[2]>=(int)self.greenslider.value) {
                bufptr[2]=200;
            }
            if (bufptr[1]>=(int)self.blueslider.value) {
                bufptr[1]=0;
            }

            bufptr += 4;
        }
    }

    // create a new CGImage from the context with modified pixels
    CGImageRef resultImage = CGBitmapContextCreateImage(context);

    // release resources to free up memory
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    free(pixels);

    return resultImage;
}

So I came across GPUImage which seems to have VERY good performance. I think I need to use GPUImageColorMatrixFilter but I can't seem to understand how exactly the colorMatrix are formed.

For example, for sepia, I was able to get this to work by looking at examples:

GPUImagePicture *gpuImage = [[GPUImagePicture alloc] initWithImage:image];

GPUImageColorMatrixFilter *conversionFilter = [[GPUImageColorMatrixFilter alloc] init];
conversionFilter.colorMatrix = (GPUMatrix4x4){
    {0.3588, 0.7044, 0.1368, 0.0},
    {0.2990, 0.5870, 0.1140, 0.0},
    {0.2392, 0.4696, 0.0912 ,0.0},
    {0,0,0,1.0},
};

[gpuImage addTarget:conversionFilter];
[conversionFilter useNextFrameForImageCapture];
[gpuImage processImage];
UIImage *toReturn = [conversionFilter imageFromCurrentFramebufferWithOrientation:0];
return toReturn;

I can't seem to understand the formation of conversionFilter.colorMatrix line. Why were those values used in that matrix?

How can I get the values for my sort of need where I need to replace threshold values with other values?

qwhuang commented 5 years ago

GPUImageRGBReplaceFilter.h

#import "GPUImageFilter.h"

@interface GPUImageRGBReplaceFilter : GPUImageFilter

@end

GPUImageRGBReplaceFilter.m

#import "GPUImageRGBReplaceFilter.h"

@implementation GPUImageRGBReplaceFilter

#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
NSString *const kGPUImageRGBReplaceFragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;

 void main()
 {
     lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
     if (textureColor.r >= 0.078) textureColor.r = 0.392;
     if (textureColor.g >= 0.392) textureColor.g = 0.784;
     if (textureColor.b >= 0.157) textureColor.b = 0.000;
     gl_FragColor = textureColor;
 }
);
#else
NSString *const kGPUImageRGBReplaceFragmentShaderString = SHADER_STRING
(
 varying vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;

 void main()
 {
     vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
     if (textureColor.r >= 0.078) textureColor.r = 0.392;
     if (textureColor.g >= 0.392) textureColor.g = 0.784;
     if (textureColor.b >= 0.157) textureColor.b = 0.000;
     gl_FragColor = textureColor.rgb;
 }
);
#endif

#pragma mark -
#pragma mark Initialization and teardown

- (id)init;
{
    if (!(self = [super initWithFragmentShaderFromString:kGPUImageRGBReplaceFragmentShaderString]))
    {
        return nil;
    }

    return self;
}

@end