【问题标题】:How to get the RGB values for a pixel on an image on the iphone如何获取iphone上图像上像素的RGB值
【发布时间】:2010-09-13 17:48:03
【问题描述】:

我正在编写一个 iPhone 应用程序,需要在本质上实现与 Photoshop 中的“吸管”工具等效的东西,您可以在其中触摸图像上的一个点并捕获相关像素的 RGB 值以确定并匹配其颜色.获取 UIImage 是很容易的部分,但是有没有办法将 UIImage 数据转换为位图表示,我可以在其中提取给定像素的这些信息?一个工作代码示例将不胜感激,请注意,我不关心 alpha 值。

【问题讨论】:

    标签: iphone cocoa-touch image bitmap


    【解决方案1】:

    您不能直接访问 UIImage 的位图数据。

    您需要获取 UIImage 的 CGImage 表示。然后从位图的 CFData 表示中获取 CGImage 的数据提供程序。确保完成后释放 CFData。

    CGImageRef cgImage = [image CGImage];
    CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
    CFDataRef bitmapData = CGDataProviderCopyData(provider);
    

    您可能希望查看 CGImage 的位图信息以获取像素顺序、图像尺寸等。

    【讨论】:

      【解决方案2】:

      为了在我的应用程序中做类似的事情,我创建了一个小的屏幕外 CGImageContext,然后将 UIImage 渲染到其中。这使我能够快速地一次提取多个像素。这意味着您可以将目标位图设置为您认为易于解析的格式,并让 CoreGraphics 完成在颜色模型或位图格式之间转换的艰苦工作。

      【讨论】:

        【解决方案3】:

        Lajos 的回答对我有用。要将像素数据作为字节数组获取,我这样做了:

        UInt8* data = CFDataGetBytePtr(bitmapData);

        更多信息:CFDataRef documentation

        另外,记得添加CoreGraphics.framework

        【讨论】:

        • 请记住,CFDataGetBytePtr(bitmapData) 返回一个 const UInt 8*,它是一个 const,因此修改它可能会导致不可预测性。
        【解决方案4】:

        我不知道如何根据给定的 X、Y 坐标正确索引图像数据。有人知道吗?

        pixelPosition = (x+(y*((imagewidth)*BytesPerPixel)));

        //据我所知,这个设备的音高不是问题,可以为零... //(或退出数学)。

        【讨论】:

          【解决方案5】:

          更详细一点...

          我今天晚上早些时候发布了一个合并和小补充的内容,该页面上已经说过 - 可以在这篇文章的底部找到。但是,此时我正在编辑帖子,以发布我建议的内容(至少对于我的要求,包括修改像素数据)是一种更好的方法,因为它提供了可写数据(而据我了解,提供的方法由以前的帖子和这篇文章的底部提供了对数据的只读引用)。

          方法一:可写像素信息

          1. 我定义了常量

            #define RGBA        4
            #define RGBA_8_BIT  8
            
          2. 在我的 UIImage 子类中,我声明了实例变量:

            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;
            
          3. 像素结构(此版本中带有 alpha)

            typedef struct RGBAPixel {
                byte red;
                byte green;
                byte blue;
                byte alpha;
            } RGBAPixel;
            
          4. 位图函数(返回预先计算的 RGBA;将 RGB 除以 A 得到未修改的 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;
            }
            

          只读数据(以前的信息)-方法2:


          第 1 步。我为字节声明了一个类型:

           typedef unsigned char byte;
          

          第二步,我声明了一个结构体对应一个像素:

           typedef struct RGBPixel{
              byte red;
              byte green;
              byte blue;  
              }   
          RGBPixel;
          

          第 3 步。我将 UIImageView 子类化并声明(具有相应的综合属性):

          //  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;
          

          步骤 4. 子类代码我放在一个名为 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;
          

          第 5 步。我制作了一个访问器方法:

          -(RGBPixel*)pixelDataForRow:(int)row column:(int)column{
              //Return a pointer to the pixel data
              return &pixelData[row * column];           
          }
          

          【讨论】:

          • 非常详细,但这个答案可以做一些整理。代码需要正确标记为代码,以便正确显示。我会自己做,但我还没有能力编辑其他人的答案。
          • 这需要针对视网膜显示器进行修改。 UIImage 的大小以点为单位,而不是像素。大小需要乘以缩放因子(self.scale)。
          【解决方案6】:

          使用ANImageBitmapRep 提供像素级访问(读/写)。

          【讨论】:

            【解决方案7】:

            谢谢大家!将其中一些答案放在一起,我得到:

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

            这只是 x 和 y 的点范围 0.0-1.0。示例:

            UIColor* sampledColor = [self colorFromImage:image
                     sampledAtPoint:CGPointMake(p.x/imageView.frame.size.width,
                                                p.y/imageView.frame.size.height)];
            

            这对我很有用。我做了几个假设,比如每像素位数和 RGBA 颜色空间,但这应该适用于大多数情况。

            另一个注意事项 - 它适用于我的模拟器和设备 - 我过去遇到过问题,因为它在设备上发生的 PNG 优化。

            【讨论】:

            • 嗯。除了透明度之外,这似乎可行。我添加了转换为 HSBA 和 RGBA 组件的方法,当我查询图像的透明部分(alpha=0.0)时,我实际得到的是 HSBA 和 RGBA 的 0,0,0,1.0 而不是 ?,? ,?,0.0 正如我所料。你对此有什么解释吗?我可以检查前三个分量是否为 0.0,但这与黑色无法区分。编辑:我看到你已经硬编码了 alpha。有什么方法可以从数据中获取它?
            • 我想通了并编辑了您的解决方案以正确处理 alpha。
            • 我使用基于此解决方案的代码的项目似乎存在内存泄漏。我真的不明白这里的内存发生了什么。也许有人可以指出我的解释。我省略了 CFRelease 语句,因为我认为 ARC 不需要它。添加它并不能解决泄漏问题,但我还无法辨别是什么耗尽了所有内存。如果我对这里的内存情况有更好的了解,我认为这可能会帮助我找到问题所在。
            • 在分配工具中显示为 UIDeviceRGBColor - 32 字节增长实例(大量)。
            • 我一直在我的设备上使用该仪器,即视网膜 iPad。切换到 ipad 模拟器(选择了视网膜)我没有看到与之前看到的相同的泄漏。我是使用乐器的新手。我是否应该期望看到我的设备与模拟器不同的结果?我应该相信哪个?
            【解决方案8】:

            这是我对 UIImage 进行颜色采样的解决方案。

            这种方法将请求的像素渲染到一个 1px 大的 RGBA 缓冲区中,并将结果颜色值作为 UIColor 对象返回。这比我见过的大多数其他方法快得多,并且只使用很少的内存。

            这对于像颜色选择器这样的东西应该很有效,在任何给定时间,您通常只需要一个特定像素的值。

            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 
            

            【讨论】:

            • 我做了一些时间戳测试,这段代码实际上比stackoverflow.com/questions/1911360/…更快
            • 我在使用 UIGraphicsBeginImageContextCGContextAddArc 等创建的图像上使用另一种方法得到了意想不到的结果,该图像属性以 nil 开头。图像显示正常,但我无法从某个点获得颜色。所以我尝试了这种方法,我得到了 r、g、b 和 192 的全零,不仅是空白图像,甚至在它被圆圈淹没之后也是如此。我认为这个类别会起作用,因为您正在强制使用您想要的色彩空间等,但它不起作用。有什么想法吗?
            • 我收回了。 r,g,b 始终为 0。每个会话的 Alpha 似乎是随机的。
            • @Matej,我发现了我的问题。我使用的是视网膜设备,因此图像的比例因子为 2。我建议在此方法中添加一个参数 scale,将其乘以 position.x 和 position.y 以获得图像中的正确位置以进行检查颜色。
            • 编辑代码以包含 self.scale 的乘法。不需要额外的参数,因为 scale 是 UIImage 对象的属性。
            猜你喜欢
            • 1970-01-01
            • 2011-12-10
            • 2020-06-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-10-04
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多