Objective C traverse pixels in an image vertically - objective-c

I'm a little confused at the moment, first time poster here on stack overflow. I'm brand new to objective C but have learned a lot from my coworkers. What I'm trying to do is traverse a bmContext vertically shifting horizontally by 1 pixel after every vertical loop. Heres some code:
NSUInteger width = image.size.width;
NSUInteger height = image.size.height;
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = width * bytesPerPixel;
NSUInteger bytesPerColumn = height * bytesPerPixel;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = height}, image.CGImage);
UInt8* data = (UInt8*)CGBitmapContextGetData(bmContext);
const size_t bitmapByteCount = bytesPerRow * height;
struct Color {
UInt8 r;
UInt8 g;
UInt8 b;
};
for (size_t i = 0; i < bytesPerRow; i += 4) //shift 1 pixel
{
for (size_t j = 0; j < bitmapByteCount; j += bytesPerRow) //check every pixel in column
{
struct Color thisColor = {data[j + i + 1], data[j + i + 2], data[j + i + 3]};
}
}
in java it looks something like this, but I have no interest in the java version it's just to emphasis my true question. I only care about the objective c code.
for (int x = 0; x = image.getWidth(); x++)
{
for (int y = 0; y = image.getHeight(); y++)
{
int rgb = image.getRGB(x, y);
//do something with pixel
}
}
Am I really shifting one unit horizontally and then checking all vertical pixels and then shifting again horizontally? I thought I was but my results seem to be a little off. In java and c# achieving a task was rather simple, if anyone knows a simpler way to do this in Objective C please let me know. Thanks in advance!

The way you are getting at the pixels seems to be off.
If I'm understanding correctly, you just want to iterate through every pixel in the image, column by column. Right?
This should work:
for (size_t i = 0; i < CGBitmapContextGetWidth(bmContext); i++)
{
for (size_t j = 0; j < CGBitmapContextGetHeight(bmContext); j++)
{
int pixel = j * CGBitmapContextGetWidth(bmContext) + i;
struct Color thisColor = {data[pixel + 1], data[pixel + 2], data[pixel + 3]};
}
}

Related

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 this
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.

Log brightness of pixels in NSImage

My goal is to find the brightness of every pixel in an image and then append it into an array. I have looked online (since this is way outside of my comfort zone, but I figured that's how I'll learn), and found several examples that get the pixel colors in Swift for IOS using UIImage. Unfortunately the same code can't be used for OS X since NSImage doesn't seem to convert to CGImage the same way a UIImage does. So that code became next to useless for me. Next I found this site, which offers a piece of example code that finds and logs the brightness of each pixel in an image. Bingo! The only problem is it is in Objective C, which I only slightly understand. After a bit more failed searches, I began attempting to translate the Objective C code. This is the original code:
- (void)setupWithImage:(UIImage*)image {
UIImage * fixedImage = [image imageWithFixedOrientation];
self.workingImage = fixedImage;
self.mainImageView.image = fixedImage;
// Commence with processing!
[self logPixelsOfImage:fixedImage];
}
- (void)logPixelsOfImage:(UIImage*)image {
// 1. Get pixels of image
CGImageRef inputCGImage = [image CGImage];
NSUInteger width = CGImageGetWidth(inputCGImage);
NSUInteger height = CGImageGetHeight(inputCGImage);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
UInt32 * pixels;
pixels = (UInt32 *) calloc(height * width, sizeof(UInt32));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixels, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast|kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), inputCGImage);
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
#define Mask8(x) ( (x) & 0xFF )
#define R(x) ( Mask8(x) )
#define G(x) ( Mask8(x >> 8 ) )
#define B(x) ( Mask8(x >> 16) )
// 2. Iterate and log!
NSLog(#"Brightness of image:");
UInt32 * currentPixel = pixels;
for (NSUInteger j = 0; j < height; j++) {
for (NSUInteger i = 0; i < width; i++) {
UInt32 color = *currentPixel;
printf("%3.0f ", (R(color)+G(color)+B(color))/3.0);
currentPixel++;
}
printf("\n");
}
free(pixels);
#undef R
#undef G
#undef B
}
And this is my translated code:
func logPixelsOfImage(image: NSImage) {
var inputCGImage: CGImageRef = image
var width: UInt = CGImageGetWidth(inputCGImage)
var height: UInt = CGImageGetHeight(inputCGImage)
var bytesPerPixel: UInt = 4
var bytesPerRow: UInt = bytesPerPixel * width
var bitsPerComponent: UInt = 8
var pixels: UInt32!
//pixels = (UInt32 *) calloc(height * width, sizeof(UInt32));
var colorSpace: CGColorSpaceRef = CGColorSpaceCreateDeviceRGB()
var context: CGContextRef = CGBitmapContextCreate(pixels, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big) //Error: Use of unresolved identifier 'kCGImageAlphaPremultipliedLast' Second Error: Use of unresolved identifier 'kCGBitmapByteOrder32Big'
CGContextDrawImage(context, CGRectMake(0, 0, width, height), inputCGImage) // Error: 'Uint' is not convertible to 'CGFloat'
/*
#define Mask8(x) ( (x) & 0xFF )
#define R(x) ( Mask8(x) )
#define G(x) ( Mask8(x >> 8 ) )
#define B(x) ( Mask8(x >> 16) )
*/
println("Brightness of image:")
var currentPixel: UInt32 = pixels
for (var j : UInt = 0; j < height; j++) {
for ( var i : UInt = 0; i < width; i++) {
var color: UInt32 = currentPixel
//printf("%3.0f ", (R(color)+G(color)+B(color))/3.0);
currentPixel++
}
println("/n")
}
//free(pixels)
/*
#undef R
#undef G
#undeg B
*/
}
Comments contain (a) errors that the code provides or (b) code that was in the original that I don't know how to translate.
So what's my question?
Is there a better way of finding the brightness of a pixel than what is shown in the Objective-C code? If so, how would you do that? If not, how would I go about finishing up the translation of the original code?
Thanks -- A CodeIt that is extremely confused and every so slightly desperate.

From char* array to two dimentional array and back algorithm goes wrong

I think my algorithm has flawed logic somewhere. Calling the two functions should return the same image however it doesn't! Can anyone see where my logic goes wrong?
These functions are used on PNG-images, I have found that they store colors as follows: ALPHA, RED, GREEN, BLUE. Repeatingly for the whole image. "pixels" is just a long array of those values (like a list).
My intent is to do a lowpass filter on the image, which is a lot easier logic if you instead use a two dimentional array / matrix of the image.
// loading pixels
UIImage *image = imageView.image;
CGImageRef imageRef = image.CGImage;
NSData *data = (NSData *)CGDataProviderCopyData(CGImageGetDataProvider(imageRef));
char *pixels = (char *)[data bytes];
// editing image
char** matrix = [self mallocMatrix:pixels withWidth:CGImageGetWidth(imageRef) andHeight:CGImageGetHeight(imageRef)];
char* newPixels = [self mallocMatrixToList:matrix withWidth:CGImageGetWidth(imageRef) andHeight:CGImageGetHeight(imageRef)];
pixels = newPixels;
and the functions looks like this:
- (char**)mallocMatrix:(char*)pixels withWidth:(int)width andHeight:(int)height {
char** matrix = malloc(sizeof(char*)*height);
int c = 0;
for (int h=0; h < height; h++) {
matrix[h] = malloc(sizeof(char)*width*4);
for (int w=0; w < (width*4); w++) {
matrix[h][w] = pixels[c];
c++;
}
}
return matrix;
}
- (char*)mallocMatrixToList:(char**)matrix withWidth:(int)width andHeight:(int)height {
char* pixels = malloc(sizeof(char)*height*width*4);
int c = 0;
for (int h=0; h < height; h++) {
for (int w=0; w < (width*4); w++) {
pixels[c] = matrix[h][w];
c++;
}
}
return pixels;
}
Edit: Fixed the malloc as posters pointed out. Simplified the algorithm a bit.
I have not tested your code but it appears you are allocating the incorrect size for your matrix and low pass filter as well as not moving to the next pixel correctly.
- (char**) mallocMatrix:(char*)pixels withWidth:(int)width andHeight:(int)height {
//When using Objective-C do not cast malloc (only do so with Objective-C++)
char** matrix = malloc(sizeof(char*)*height);
for (int h=0; h < height; h++) {
//Each row needs to malloc the sizeof(char) not char *
matrix[h] = malloc(sizeof(char)*width*4);
for (int w=0; w < width; w++) {
// Varje pixel har ARGB
for (int i=0; i < 4; i++) {
matrix[h][w+i] = pixels[h*w+i];
}
}
}
return matrix;
}
- (char*) mallocLowPassFilter:(char**)matrix withWidth:(int)width andHeight:(int)height
{
//Same as before only malloc sizeof(char)
char* pixels = malloc(sizeof(char)*height*width*4);
for (int h=0; h < height; h++) {
for (int w=0; w < width; w++) {
// Varje pixel har ARGB
for (int i=0; i < 4; i++) {
// TODO: Lowpass here
pixels[h*w+i] = matrix[h][w+i];
}
}
}
return pixels;
}
Note: This code, as you know, is limited to ARGB images. If you would like to support more image formats there are additional functions available to get more information about your image such as CGImageGetColorSpace to find the pixel format (ARGB, RGBA, RGB, etc...), and CGImageGetBytesPerRow to get the number of bytes per row (you wouldn't have to multiply width by channels per pixel).

Reading PVRTC image color information for each pixel

How do I read the image color information for each pixel of PVRTC image?
Here is my code extracting the integer arrays
NSData *data = [[NSData alloc] initWithContentsOfFile:path];
NSMutableArray *_imageData = [[NSMutableArray alloc] initWithCapacity:10];
BOOL success = FALSE;
PVRTexHeader *header = NULL;
uint32_t flags, pvrTag;
uint32_t dataLength = 0, dataOffset = 0, dataSize = 0;
uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
uint32_t width = 0, height = 0, bpp = 4;
uint8_t *bytes = NULL;
uint32_t formatFlags;
header = (PVRTexHeader *)[data bytes];
pvrTag = CFSwapInt32LittleToHost(header->pvrTag);
if (gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) ||
gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) ||
gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) ||
gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff))
{
return FALSE;
}
flags = CFSwapInt32LittleToHost(header->flags);
formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
if (formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2)
{
[_imageData removeAllObjects];
if (formatFlags == kPVRTextureFlagTypePVRTC_4)
_internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
else if (formatFlags == kPVRTextureFlagTypePVRTC_2)
_internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
_width = width = CFSwapInt32LittleToHost(header->width);
_height = height = CFSwapInt32LittleToHost(header->height);
if (CFSwapInt32LittleToHost(header->bitmaskAlpha))
_hasAlpha = TRUE;
else
_hasAlpha = FALSE;
dataLength = CFSwapInt32LittleToHost(header->dataLength);
bytes = ((uint8_t *)[data bytes]) + sizeof(PVRTexHeader);
// Calculate the data size for each texture level and respect the minimum number of blocks
while (dataOffset < dataLength)
{
if (formatFlags == kPVRTextureFlagTypePVRTC_4)
{
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4;
heightBlocks = height / 4;
bpp = 4;
}
else
{
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8;
heightBlocks = height / 4;
bpp = 2;
}
// Clamp to minimum number of blocks
if (widthBlocks < 2)
widthBlocks = 2;
if (heightBlocks < 2)
heightBlocks = 2;
dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
[_imageData addObject:[NSData dataWithBytes:bytes+dataOffset length:dataSize]];
for (int i=0; i < mipmapCount; i++)
{
NSLog(#"width:%d, height:%d",width,height);
data = [[NSData alloc] initWithData:[_imageData objectAtIndex:i]];
NSLog(#"data length:%d",[data length]);
//extracted 20 sample data, but all u could see are large integer number
for(int i = 0; i < 20; i++){
NSLog(#"data[%d]:%d",i,data[i]);
}
PVRTC is a 4x4 (or 8x4) texel, block-based compression system that takes into account surrounding blocks to represent two low frequency images with which higher frequency modulation data is combined in order to produce the actual texel output. A better explanation is available here:
http://web.onetel.net.uk/~simonnihal/assorted3d/fenney03texcomp.pdf
So the values you're extracting are actually parts of the encoded blocks and these need to be decoded correctly in order to get sensible values.
There are two ways to get to the colour information: decode/decompress the PVR texture information using a software decompressor or render the texture using a POWERVR graphics core and then read the result back. I'll only discuss the first option here.
It's rather tricky to assemble a decompressor from only the information there, but fortunately there's C++ decompression source code in the POWERVR SDK which you can get here - download one of the iPhone SDKs for instance:
http://www.imgtec.com/powervr/insider/powervr-sdk.asp
It's in the Tools/PVRTDecompress.cpp file.
Hope that helps.

Using the contents of an array to set individual pixels in a Quartz bitmap context

I have an array that contains the RGB colour values for each pixel in a 320 x 180 display. I would like to be able to set individual pixel values in the a bitmap context of the same size offscreen then display the bitmap context in a view.
It appears that I have to create 1x1 rects and either put a stroke on them or a line of length 1 at the point in question. Is that correct? I'm looking for a very efficient way of getting the array data onto the graphics context as you can imagine this is going to be an image buffer that cycles at 25 frames per second and drawing in this way seems inefficient.
I guess the other question is should I use OPENGL ES instead?
Thoughts/best practice would be much appreciated.
Regards
Dave
OK, have come a short way, but can't make the final hurdle and I am not sure why this isn't working:
- (void) displayContentsOfArray1UsingBitmap: (CGContextRef)context
{
long bitmapData[WIDTH * HEIGHT];
// Build bitmap
int i, j, h;
for (i = 0; i < WIDTH; i++)
{
for (j = 0; j < HEIGHT; j++)
{
h = frameBuffer01[i][j];
bitmapData[i * j] = h;
}
}
// Blit the bitmap to the context
CGDataProviderRef providerRef = CGDataProviderCreateWithData(NULL, bitmapData,4 * WIDTH * HEIGHT, NULL);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGImageRef imageRef = CGImageCreate(WIDTH, HEIGHT, 8, 32, WIDTH * 4, colorSpaceRef, kCGImageAlphaFirst, providerRef, NULL, YES, kCGRenderingIntentDefault);
CGContextDrawImage(context, CGRectMake(0.0, HEIGHT, WIDTH, HEIGHT), imageRef);
CGImageRelease(imageRef);
CGColorSpaceRelease(colorSpaceRef);
CGDataProviderRelease(providerRef);
}
Read the documentation for CGImageCreate(). Basically, you have to create a CGDataProvider from your pixel array (using CGDataProviderCreateDirect()), then create a CGImage with this data provider as a source. You can then draw the image into any context. It's a bit tedious to get this right because these functions expect a lot of arguments, but the documentation is quite good.
Dave,
The blitting code works fine, but your code to copy from the frame buffer is incorrect.
// Build bitmap
int i, j, h;
for (i = 0; i < WIDTH; i++)
{
for (j = 0; j < HEIGHT; j++)
{
h = frameBuffer01[i][j];
bitmapData[/*step across a line*/i + /*step down a line*/j*WIDTH] = h;
}
}
Note my changes to the assignment to elements of bitmapData.
Not knowing the layout of frame, this may still be incorrect, but from your code, this looks closer to the intent.