Bohdan Savych Bohdan Savych - 6 months ago 35
Objective-C Question

Convolution matrix sharpen filter

i trying to implement sharpen convolution matrix filter for image.For this i create matrix 3x3. Maybe i did something wrong with formula?Also i tried other sharpen matrix but it didnt help. Color value could be larger then 255 or smaller then zero so i decide to give some limits on this(0 255).Is it correct solution?

static const int filterSmallMatrixSize = 3;
static const int sharpMatrix[3][3] = {{-1, -1, -1},{-1, 9, -1},{-1, -1, -1}};


some define

#define Mask8(x) ( (x) & 0xFF )
#define R(x) ( Mask8(x) )
#define G(x) ( Mask8(x >> 8 ) )
#define B(x) ( Mask8(x >> 16) )
#define A(x) ( Mask8(x >> 24) )
#define RGBAMake(r, g, b, a) ( Mask8(r) | Mask8(g) << 8 | Mask8(b) << 16 | Mask8(a) << 24 )


and algorithm

- (UIImage *)processSharpFilterUsingPixels:(UIImage *)inputImage
{
UInt32 *inputPixels;
CGImageRef inputCGImage = [inputImage CGImage];
NSUInteger inputWidth = CGImageGetWidth(inputCGImage);
NSUInteger inputHeight = CGImageGetHeight(inputCGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4;
NSUInteger bitsPerComponent = 8;
NSUInteger inputBytesPerRow = bytesPerPixel * inputWidth;
inputPixels = (UInt32 *)calloc(inputHeight * inputWidth, sizeof(UInt32));
CGContextRef context = CGBitmapContextCreate(inputPixels, inputWidth, inputHeight,
bitsPerComponent, inputBytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, inputWidth, inputHeight), inputCGImage);

for (NSUInteger j = 1; j < inputHeight - 1; j++)
{
for (NSUInteger i = 1; i < inputWidth - 1; i++)
{
Float32 newRedColor = 0;
Float32 newGreenColor = 0;
Float32 newBlueColor = 0;
Float32 newA = 0;

for (int filterMatrixI = 0 ; filterMatrixI < filterSmallMatrixSize ; filterMatrixI ++)
{
for (int filterMatrixJ = 0; filterMatrixJ < filterSmallMatrixSize; filterMatrixJ ++)
{
UInt32 * currentPixel = inputPixels + ((j + filterMatrixJ - 1) * inputWidth) + i + filterMatrixI - 1;
int color = *currentPixel;
newRedColor += (R(color) * sharpMatrix[filterMatrixI][filterMatrixJ]);
newGreenColor += (G(color) * sharpMatrix[filterMatrixI][filterMatrixJ]);
newBlueColor += (B(color)* sharpMatrix[filterMatrixI][filterMatrixJ]);
newA += (A(color) * sharpMatrix[filterMatrixI][filterMatrixJ]);
}
}
int r = MAX( MIN((int)newRedColor,255), 0);
int g = MAX( MIN((int)newGreenColor,255), 0);
int b = MAX( MIN((int)newBlueColor,255), 0);
int a = MAX( MIN((int)newA,255), 0);
UInt32 *currentMainImagePixel = inputPixels + (j * inputWidth) + i;
*currentMainImagePixel = RGBAMake(r,g,b,a);
}
}

CGImageRef newCGImage = CGBitmapContextCreateImage(context);
UIImage * processedImage = [UIImage imageWithCGImage:newCGImage];

CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
free(inputPixels);

return processedImage;
}


As result i have thisthis

Answer

Consider these are pixels in the middle of image:

|_|_|_|_|
|_|_|_|_|
|_|_|_|_|
|_|_|_|_|

Since you are updating image in place, this is how it looks somewhere in the middle of sharpen cycle:

|u|u|u|u|
|u|u|u|u|
|u|c|_|_|
|_|_|_|_|

Where u stands for updated pixel, c for current. So his new color depends on color of surround pixels, half of which are from already sharpened image, half from origin. To fix it we need a copy of original image's pixels:

...
CGContextDrawImage(context, CGRectMake(0, 0, inputWidth, inputHeight), inputCGImage);

UInt32 *origPixels = calloc(inputHeight * inputWidth, sizeof(UInt32));
memcpy(origPixels, inputPixels, inputHeight * inputWidth * sizeof(UInt32));

for (NSUInteger j = 1; j < inputHeight - 1; j++) {
...

And now we only need to change one line to get our current pixels from original image

//changed inputPixels -> origPixels
UInt32 * currentPixel = origPixels + ((j + filterMatrixJ - 1) * inputWidth) + i + filterMatrixI - 1;

Here are some examples of how it works compared to not updated filter (link is dropbox, sorry about that). I've tried different matrices, and as for me the best was somewhere around

const float sharpMatrix[3][3] = {{-0.3, -0.3, -0.3},{-0.3, 3.4, -0.3},{-0.3, -0.3, -0.3}}

Also, I need to notice that this way of keeping original image is not optimal. My fix basically doubles amount of memory consumed. It could be easily done via holding only two lines of pixels, and I'm sure there are even better ways.

Comments