代码之家  ›  专栏  ›  技术社区  ›  Peter

如何在iPhone上获取图像像素的RGB值

  •  37
  • Peter  · 技术社区  · 16 年前

    我正在编写一个iPhone应用程序,基本上需要在photoshop中实现类似于“滴管”工具的工具,在该工具中,您可以触摸图像上的一个点,并捕获相关像素的RGB值,以确定和匹配其颜色。获取uiimage是一个简单的部分,但是有没有一种方法可以将uiimage数据转换为位图表示,在位图表示中我可以提取给定像素的信息?一个有效的代码示例将是最受欢迎的,请注意,我不关心alpha值。

    8 回复  |  直到 8 年前
        1
  •  38
  •   dmaulikr    8 年前

    更多细节…

    今天晚上早些时候,我发表了一篇文章,对本页所说的内容进行了整合和补充,可以在文章的底部找到。不过,我现在正在编辑文章,以发布我建议的(至少对于我的要求,包括修改像素数据)更好的方法,因为它提供了可写数据(而据我所知,以前的文章和本文底部提供的方法提供了对数据的只读引用)。

    方法1:可写像素信息

    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. 位图函数(返回预先计算的rgb a;将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;
    

    步骤2。我声明了一个结构来对应一个像素:

     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的方法中的子类代码(返回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];           
    }
    
        2
  •  22
  •   Matej Bukovinski    12 年前

    这是我对uiimage颜色采样的解决方案。

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

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

    uiimage+选取器.h

    #import <UIKit/UIKit.h>
    
    
    @interface UIImage (Picker)
    
    - (UIColor *)colorAtPosition:(CGPoint)position;
    
    @end
    

    uiimage+选取器.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 
    
        3
  •  11
  •   lajos    16 年前

    无法直接访问uiimage的位图数据。

    您需要获得uiimage的cgimage表示。然后从位图的cfdata表示中获取cgimage的数据提供者。完成后确保释放cfdata。

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

    您可能需要查看CGIMAGE的位图信息以获取像素顺序、图像尺寸等。

        4
  •  5
  •   yes123    16 年前

    拉霍斯的回答对我很有帮助。为了获得字节数组形式的像素数据,我执行了以下操作:

    UInt8* data = CFDataGetBytePtr(bitmapData);

    更多信息:cfdataref documentation .

    另外,记住要包括 CoreGraphics.framework

        5
  •  3
  •   garafajon    12 年前

    谢谢大家!把这些答案结合起来,我得到:

    - (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优化。

        6
  •  1
  •   Mark Bessey    16 年前

    为了在我的应用程序中执行类似的操作,我创建了一个小的屏幕外CGImageContext,然后将uiImage渲染到其中。这使我能够快速提取大量像素。这意味着您可以以易于解析的格式设置目标位图,并让Coregraphics在颜色模型或位图格式之间进行转换。

        7
  •  1
  •   me here    16 年前

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

    像素位置=(X+(Y*(图像宽度)*字节像素));

    //据我所知,这个设备的音高不是问题,可以调零… //(或者从数学中抽出来)。

        8
  •  1
  •   smdvlpr    13 年前

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