Reading PVRTC image color information for each pixel - objective-c

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.

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.

NSCalibratedRGBColor chewing up memory in mosaic program

Firstly, I am a newbie so my thanks for your patience.
I am having a go at developing my own photo mosaic program - for me very much a learning journey.
As part of the program I am trying to enumerate through a folder of images, to calculate the average RGB of the images pixels, to store this data in a NSDictionary object and then collect the NSDictionary for each image in an array.
The code is a bit of a bull doze approach at the moment as I try and learn things - and I don't have any error catching built in yet.
The code works , however, when I run this through instruments I get huge allocation to a massive number of NSCalibratedRGBColor objects which don't seem to be released at the end of the routine.
Any thoughts/guidance on how to handle this memory issue would be welcome.
I am using the following code for calculating the average RGB.
while (tempFile = [dirEnum nextObject]) {
//need to process the images within this while loop
if ([filesAllowed containsObject:[tempFile pathExtension]]) {
count = count + 1;
test = [NSString stringWithFormat:#"%#/%#", imageSourceFilePath, tempFile];
tempFileURL = [NSURL fileURLWithPath:test];
CIImage *sourceCIImage = [[CIImage alloc] initWithContentsOfURL:tempFileURL];
NSBitmapImageRep *tempBitmapImageRep = [[NSBitmapImageRep alloc] initWithCIImage:sourceCIImage];
NSInteger imageHeight = [tempBitmapImageRep pixelsHigh];
NSInteger imageWidth = [tempBitmapImageRep pixelsWide];
int totalPixels = (int)imageWidth * (int)imageHeight;
countPixelsWide = 0;
countPixelsHigh = 0;
totalGreen = 0;
totalBlue = 0;
totalRed = 0;
//work out averages - iterate over each column
while (countPixelsWide < imageWidth) {
while (countPixelsHigh < imageHeight) {
tempColor = [tempBitmapImageRep colorAtX:countPixelsWide y:countPixelsHigh];
[tempColor getRed:&tempRed green:&tempGreen blue:&tempBlue alpha:&tempAlpha];
totalRed = totalRed + tempRed;
totalBlue = totalBlue + tempBlue;
totalGreen = totalGreen + tempGreen;
countPixelsHigh = countPixelsHigh + 1;
}
countPixelsWide = countPixelsWide + 1;
countPixelsHigh = 0;
}
//calculate averages - re-use total variables
totalRed = totalRed/totalPixels;
totalBlue = totalBlue/totalPixels;
totalGreen = totalGreen/totalPixels;
//create dictionary for each source image and then add to sourceImageArray for later reference
dictOfSourceImages = [self makeDictionaryRecord:tempFileURL withAverageRed:[NSNumber numberWithFloat:totalRed] withAverageBlue:[NSNumber numberWithFloat:totalBlue] withAverageGreen:[NSNumber numberWithFloat:totalGreen]];
[sourceImageData addObject:dictOfSourceImages];
}
}

Create monochrome CGImageRef (1-bit per pixel bitmap)

I'm trying to show a preview of an image in 1-bit monochrome, as in, not grayscale, but bitonal black and white. It's supposed to be an indication of how the image will look if it were faxed. Formats as low as 1-bit per pixel aren't available on OS X, only 8-bit grayscale. Is there any way to achieve this effect using Core Graphics or another framework (ideally with dithering)?
I know there's a filter called CIColorMonochrome but this only converts the image to grayscale.
The creation of a 1 bit deep NSImageRep (and also in the CG-world) is AFAIK not supported, So we have to do it manually. It might be useful to use CIImage for this task. Here I go the classical (you may call it old-fashioned) way. Here is a code that shows how we can do it. First a gray image is created from an NSImageRep so we have a well defined and simple format whatever the source image will be formatted (could also be a PDF file). The resulting gray image is the source for the bitonal image. Here is the code for creating a gray image: (without respecting the size / resolution of the source image, only the pixels count!):
- (NSBitmapImageRep *) grayRepresentationOf:(NSImageRep *)aRep
{
NSBitmapImageRep *newRep =
[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:[aRep pixelsWide]
pixelsHigh:[aRep pixelsHigh]
bitsPerSample:8
samplesPerPixel:1
hasAlpha:NO //must be NO !
isPlanar:NO
colorSpaceName:NSCalibratedWhiteColorSpace
bytesPerRow:0
bitsPerPixel:0 ];
// this new imagerep has (as default) a resolution of 72 dpi
[NSGraphicsContext saveGraphicsState];
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:newRep];
if( context==nil ){
NSLog( #"*** %s context is nil", __FUNCTION__ );
return nil;
}
[NSGraphicsContext setCurrentContext:context];
[aRep drawInRect:NSMakeRect( 0, 0, [newRep pixelsWide], [newRep pixelsHigh] )];
[NSGraphicsContext restoreGraphicsState];
return [newRep autorelease];
}
In the next method we create an NXBitmapImageRep (bits per pixel=1, samples per pixel=1) from a given NSImageRep (one of it's subclasses) and will use the method just given:
- (NSBitmapImageRep *) binaryRepresentationOf:(NSImageRep *)aRep
{
NSBitmapImageRep *grayRep = [aRep grayRepresentation];
if( grayRep==nil ) return nil;
NSInteger numberOfRows = [grayRep pixelsHigh];
NSInteger numberOfCols = [grayRep pixelsWide];
NSBitmapImageRep *newRep =
[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:numberOfCols
pixelsHigh:numberOfRows
bitsPerSample:1
samplesPerPixel:1
hasAlpha:NO
isPlanar:NO
colorSpaceName:NSCalibratedWhiteColorSpace
bitmapFormat:0
bytesPerRow:0
bitsPerPixel:0 ];
unsigned char *bitmapDataSource = [grayRep bitmapData];
unsigned char *bitmapDataDest = [newRep bitmapData];
// here is the place to use dithering or error diffusion (code below)
// iterate over all pixels
NSInteger grayBPR = [grayRep bytesPerRow];
NSInteger binBPR = [newRep bytesPerRow];
NSInteger pWide = [newRep pixelsWide];
for( NSInteger row=0; row<numberOfRows; row++ ){
unsigned char *rowDataSource = bitmapDataSource + row*grayBPR;
unsigned char *rowDataDest = bitmapDataDest + row*binBPR;
NSInteger destCol = 0;
unsigned char bw = 0;
for( NSInteger col = 0; col<pWide; ){
unsigned char gray = rowDataSource[col];
if( gray>127 ) {bw |= (1<<(7-col%8)); };
col++;
if( (col%8 == 0) || (col==pWide) ){
rowDataDest[destCol] = bw;
bw = 0;
destCol++;
}
}
}
// save as PNG for testing and return
[[newRep representationUsingType:NSPNGFileType properties:nil] writeToFile:#"/tmp/bin_1.png" atomically:YES];
return [newRep autorelease];
}
For error diffusion I used the following code which changes directly the bitmap of the gray image. This is allowed because the gray image itself is no longer used.
// change bitmapDataSource : use Error-Diffusion
for( NSInteger row=0; row<numberOfRows-1; row++ ){
unsigned char *currentRowData = bitmapDataSource + row*grayBPR;
unsigned char *nextRowData = currentRowData + grayBPR;
for( NSInteger col = 1; col<numberOfCols; col++ ){
NSInteger origValue = currentRowData[col];
NSInteger newValue = (origValue>127) ? 255 : 0;
NSInteger error = -(newValue - origValue);
currentRowData[col] = newValue;
currentRowData[col+1] = clamp(currentRowData[col+1] + (7*error/16));
nextRowData[col-1] = clamp( nextRowData[col-1] + (3*error/16) );
nextRowData[col] = clamp( nextRowData[col] + (5*error/16) );
nextRowData[col+1] = clamp( nextRowData[col+1] + (error/16) );
}
}
clamp is a macro defined before the method
#define clamp(z) ( (z>255)?255 : ((z<0)?0:z) )
This makes the unsigned char bytes to have valid values (0<=z<=255)

Objective C traverse pixels in an image vertically

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]};
}
}

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