Jak uzyskać wartości RGB dla piksela na obrazie na iPhonie
Piszę aplikację na iPhone 'a i muszę zasadniczo zaimplementować coś równoważnego narzędziu "kroplomierz" w Photoshopie, gdzie można dotknąć punktu na obrazie i przechwycić wartości RGB dla danego piksela, aby określić i dopasować jego kolor. Uzyskanie interfejsu użytkownika jest łatwą częścią, ale czy istnieje sposób na przekonwertowanie danych interfejsu użytkownika na reprezentację bitmapową, w której mógłbym wyodrębnić te informacje dla danego piksela? Próbka kodu roboczego byłaby najbardziej doceniam i zauważ, że nie interesuje mnie wartość alfa.
8 answers
Trochę więcej szczegółów...
Opublikowałem dziś wieczorem z konsolidacją i małym dodatkiem do tego, co zostało powiedziane na tej stronie - które można znaleźć na dole tego postu. Edytuję post w tym momencie, jednak, aby opublikować to, co proponuję (przynajmniej dla moich wymagań, które obejmują modyfikowanie danych pikseli) lepszą metodą, ponieważ zapewnia dane zapisywalne (podczas gdy, jak rozumiem, metoda przewidziana w poprzednich postach i na dole tego postu zapewnia lepszą odniesienie tylko do odczytu danych).
Metoda 1: Zapisywalne Informacje O Pikselach
-
Zdefiniowałem stałe
#define RGBA 4 #define RGBA_8_BIT 8
-
W mojej podklasie UIImage zadeklarowałem zmienne instancji:
size_t bytesPerRow; size_t byteCount; size_t pixelCount; CGContextRef context; CGColorSpaceRef colorSpace; UInt8 *pixelByteData; // A pointer to an array of RGBA bytes in memory RPVW_RGBAPixel *pixelData;
-
Struktura pikseli (z alfą w tej wersji)
typedef struct RGBAPixel { byte red; byte green; byte blue; byte alpha; } RGBAPixel;
-
Funkcja Bitmap (zwraca wstępnie obliczone RGBA; dzieli RGB przez A, aby uzyskać niezmodyfikowane RGB):
-(RGBAPixel*) bitmap { NSLog( @"Returning bitmap representation of UIImage." ); // 8 bits each of red, green, blue, and alpha. [self setBytesPerRow:self.size.width * RGBA]; [self setByteCount:bytesPerRow * self.size.height]; [self setPixelCount:self.size.width * self.size.height]; // Create RGB color space [self setColorSpace:CGColorSpaceCreateDeviceRGB()]; if (!colorSpace) { NSLog(@"Error allocating color space."); return nil; } [self setPixelData:malloc(byteCount)]; if (!pixelData) { NSLog(@"Error allocating bitmap memory. Releasing color space."); CGColorSpaceRelease(colorSpace); return nil; } // Create the bitmap context. // Pre-multiplied RGBA, 8-bits per component. // The source image format will be converted to the format specified here by CGBitmapContextCreate. [self setContext:CGBitmapContextCreate( (void*)pixelData, self.size.width, self.size.height, RGBA_8_BIT, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast )]; // Make sure we have our context if (!context) { free(pixelData); NSLog(@"Context not created!"); } // Draw the image to the bitmap context. // The memory allocated for the context for rendering will then contain the raw image pixelData in the specified color space. CGRect rect = { { 0 , 0 }, { self.size.width, self.size.height } }; CGContextDrawImage( context, rect, self.CGImage ); // Now we can get a pointer to the image pixelData associated with the bitmap context. pixelData = (RGBAPixel*) CGBitmapContextGetData(context); return pixelData; }
Dane tylko do odczytu ( poprzednie informacje) - metoda 2:
Krok 1. I zadeklarował typ dla bajtu:
typedef unsigned char byte;
Krok 2. Zadeklarowałem strukturę odpowiadającą pikselowi:
typedef struct RGBPixel{
byte red;
byte green;
byte blue;
}
RGBPixel;
Krok 3. Podklasowałem UIImageView i zadeklarowałem (z odpowiednimi syntetyzowanymi właściwościami):
// Reference to Quartz CGImage for receiver (self)
CFDataRef bitmapData;
// Buffer holding raw pixel data copied from Quartz CGImage held in receiver (self)
UInt8* pixelByteData;
// A pointer to the first pixel element in an array
RGBPixel* pixelData;
Krok 4. Kod podklasy umieszczam w metodzie o nazwie bitmap (aby zwrócić dane o pikselach bitmap):
//Get the bitmap data from the receiver's CGImage (see UIImage docs)
[self setBitmapData: CGDataProviderCopyData(CGImageGetDataProvider([self CGImage]))];
//Create a buffer to store bitmap data (unitialized memory as long as the data)
[self setPixelBitData:malloc(CFDataGetLength(bitmapData))];
//Copy image data into allocated buffer
CFDataGetBytes(bitmapData,CFRangeMake(0,CFDataGetLength(bitmapData)),pixelByteData);
//Cast a pointer to the first element of pixelByteData
//Essentially what we're doing is making a second pointer that divides the byteData's units differently - instead of dividing each unit as 1 byte we will divide each unit as 3 bytes (1 pixel).
pixelData = (RGBPixel*) pixelByteData;
//Now you can access pixels by index: pixelData[ index ]
NSLog(@"Pixel data one red (%i), green (%i), blue (%i).", pixelData[0].red, pixelData[0].green, pixelData[0].blue);
//You can determine the desired index by multiplying row * column.
return pixelData;
Krok 5. Zrobiłem metodę accessor:
-(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
//Return a pointer to the pixel data
return &pixelData[row * column];
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-29 11:24:48
Oto moje rozwiązanie do próbkowania koloru interfejsu.
To podejście renderuje żądany piksel do dużego bufora RGBA o rozmiarze 1px i zwraca wynikowe wartości kolorów jako obiekt UIColor. Jest to znacznie szybsze niż większość innych podejść, które widziałem i wykorzystuje tylko bardzo mało pamięci.
Powinno to działać całkiem dobrze dla czegoś takiego jak próbnik kolorów, gdzie zazwyczaj potrzebujesz tylko wartości jednego określonego piksela w danym momencie.
Uiimage + Picker.h
#import <UIKit/UIKit.h>
@interface UIImage (Picker)
- (UIColor *)colorAtPosition:(CGPoint)position;
@end
Uiimage + Picker.m
#import "UIImage+Picker.h"
@implementation UIImage (Picker)
- (UIColor *)colorAtPosition:(CGPoint)position {
CGRect sourceRect = CGRectMake(position.x, position.y, 1.f, 1.f);
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, sourceRect);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *buffer = malloc(4);
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
CGContextRef context = CGBitmapContextCreate(buffer, 1, 1, 8, 4, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0.f, 0.f, 1.f, 1.f), imageRef);
CGImageRelease(imageRef);
CGContextRelease(context);
CGFloat r = buffer[0] / 255.f;
CGFloat g = buffer[1] / 255.f;
CGFloat b = buffer[2] / 255.f;
CGFloat a = buffer[3] / 255.f;
free(buffer);
return [UIColor colorWithRed:r green:g blue:b alpha:a];
}
@end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-17 09:15:31
Nie można uzyskać dostępu do danych bitmapowych interfejsu użytkownika bezpośrednio.
Musisz uzyskać reprezentację Cgimage UIImage. Następnie pobierz dostawcę danych CGImage, z którego pochodzi Reprezentacja cfdata bitmapy. Upewnij się, że zwolnisz CFData po zakończeniu.
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
Prawdopodobnie będziesz chciał spojrzeć na informacje o bitmapie CGImage, aby uzyskać kolejność pikseli, wymiary obrazu itp.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-28 01:12:55
Odpowiedź Lajosa zadziałała. Aby uzyskać dane pikseli jako tablicę bajtów, zrobiłem to:
UInt8* data = CFDataGetBytePtr(bitmapData);
Więcej informacji: cfdataref dokumentacja .
Pamiętaj również o dołączeniu CoreGraphics.framework
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-29 01:09:01
Dziękuję wszystkim! Składając kilka z tych odpowiedzi otrzymuję:
- (UIColor*)colorFromImage:(UIImage*)image sampledAtPoint:(CGPoint)p {
CGImageRef cgImage = [image CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
const UInt8* data = CFDataGetBytePtr(bitmapData);
size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
int col = p.x*(width-1);
int row = p.y*(height-1);
const UInt8* pixel = data + row*bytesPerRow+col*4;
UIColor* returnColor = [UIColor colorWithRed:pixel[0]/255. green:pixel[1]/255. blue:pixel[2]/255. alpha:1.0];
CFRelease(bitmapData);
return returnColor;
}
To tylko przyjmuje zakres punktowy 0.0 - 1.0 zarówno dla x jak i y. przykład:
UIColor* sampledColor = [self colorFromImage:image
sampledAtPoint:CGPointMake(p.x/imageView.frame.size.width,
p.y/imageView.frame.size.height)];
To mi pasuje. Robię kilka założeń, takich jak bity na piksel i przestrzeń kolorów RGBA, ale to powinno działać w większości przypadków.
Kolejna uwaga - działa zarówno na symulatorze, jak i urządzeniu - miałem z tym problemy w przeszłości z powodu optymalizacji PNG, która miała miejsce, gdy poszedł na urządzenie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-20 04:00:58
Aby zrobić coś podobnego w mojej aplikacji, stworzyłem mały cgimagecontext poza ekranem, a następnie wyrenderowałem do niego interfejs. To pozwoliło mi na szybki sposób wyodrębnić kilka pikseli na raz. Oznacza to, że można skonfigurować docelową bitmapę w formacie, który można łatwo przeanalizować, i pozwolić firmie CoreGraphics wykonywać ciężką pracę polegającą na konwersji między modelami kolorów lub formatami bitmap.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 21:37:36
Nie wiem, jak poprawnie indeksować dane obrazkowe na podstawie podanej korekcji X, Y. Czy ktoś wie?
PixelPosition = (x+(y*((imagewidth)*BytesPerPixel)));
/ / pitch nie jest problemem z tym urządzeniem o ile wiem i może być zerowy... // (lub wycofany z matematyki).
Użyj ANImageBitmapRep , który daje dostęp na poziomie pikseli (Odczyt/Zapis).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 17:15:42