【问题标题】:Can I edit the pixels of the UIImage's property CGImage我可以编辑 UIImage 属性 CGImage 的像素吗
【发布时间】:2010-11-19 20:29:39
【问题描述】:

UIImage 有一个只读属性 CGImage。我必须将其像素读取到内存块并对其进行编辑,然后制作一个新的 UIImage 来替换旧的。我想知道是否有办法绕过只读属性并直接编辑这些像素。

谢谢。


谢谢大家。我找到了一种方法。使用这些方法编写一个类:

-(void)preProcess:(UIImage*)srcImage {
    m_Context = ...// Created by calling CGBitmapContextCreate(...)
    ...
    CGContextDrawImage(m_Context, rect, srcImage.CGImage);
    m_Bits = (unsigned char*)CGBitmapContextGetData (mContext);
}

-(void)postProcess {
    CGContextRelease(m_Context);
    free(m_Bits);
}

-(UIImage*)doProcess:(CGPoint)pt {// just a example 
    unsigned char* ppxl = m_Bits + ...
    // do something...
    CGImageRef imRef = CGBitmapContextCreateImage(mContext);
    return [UIImage imageWithCGImage:imRef];
}

preProcess 和 postProcess 只调用一次。

【问题讨论】:

  • 嘿,我已经理解您给出的示例,但是可以在这里发布源代码吗?我也在尝试像提出的问题一样实施。

标签: iphone cocoa cocoa-touch uikit core-graphics


【解决方案1】:

您无法获取原始像素。但是,您可以获得副本。一种选择是按照 Matt 的建议,将其转换为 PNG/JPG - 但请记住,图像现在已压缩,您将处理压缩文件而不是直接处理像素。

如果您想获取原始像素的副本,可以执行以下操作:

UIImage* image = ...; // An image
NSData* pixelData = (NSData*) CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
void* pixelBytes = [pixelData bytes];

// Take away the red pixel, assuming 32-bit RGBA
for(int i = 0; i < [pixelData length]; i += 4) {
    bytes[i] = 0; // red
    bytes[i+1] = bytes[i+1]; // green
    bytes[i+2] = bytes[i+2]; // blue
    bytes[i+3] = bytes[i+3]; // alpha
}

现在,如果你想把它变成一个新的 UIImage,你可以这样做:

NSData* newPixelData = [NSData dataWithBytes:pixelBytes length:[pixelData length]];
UIImage* newImage = [UIImage imageWithData:newPixelData]; // Huzzah

【讨论】:

  • 我试过但失败了。 CGDataProviderCopyData 返回一个 CFDataRef,它是根据 iPhone 文档对不可变 CFData 对象的引用。
【解决方案2】:

对于我们当中比较迟钝的人(阅读:未来的我),这里有一些基于 Itay 和 Dave R 答案的工作代码。它以 UIImage 开始,以修改后的 UIImage 结束:

    // load image
    UIImage *image      = [UIImage imageNamed:@"test.png"];
    CGImageRef imageRef = image.CGImage;
    NSData *data        = (NSData *)CGDataProviderCopyData(CGImageGetDataProvider(imageRef));
    char *pixels        = (char *)[data bytes];

    // this is where you manipulate the individual pixels
    // assumes a 4 byte pixel consisting of rgb and alpha
    // for PNGs without transparency use i+=3 and remove int a
    for(int i = 0; i < [data length]; i += 4)
    {
        int r = i;
        int g = i+1;
        int b = i+2;
        int a = i+3;

        pixels[r]   = 0; // eg. remove red
        pixels[g]   = pixels[g];
        pixels[b]   = pixels[b];
        pixels[a]   = pixels[a];
    }

    // create a new image from the modified pixel data
    size_t width                    = CGImageGetWidth(imageRef);
    size_t height                   = CGImageGetHeight(imageRef);
    size_t bitsPerComponent         = CGImageGetBitsPerComponent(imageRef);
    size_t bitsPerPixel             = CGImageGetBitsPerPixel(imageRef);
    size_t bytesPerRow              = CGImageGetBytesPerRow(imageRef);

    CGColorSpaceRef colorspace      = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo         = CGImageGetBitmapInfo(imageRef);
    CGDataProviderRef provider      = CGDataProviderCreateWithData(NULL, pixels, [data length], NULL);

    CGImageRef newImageRef = CGImageCreate (
                              width,
                              height,
                              bitsPerComponent,
                              bitsPerPixel,
                              bytesPerRow,
                              colorspace,
                              bitmapInfo,
                              provider,
                              NULL,
                              false,
                              kCGRenderingIntentDefault
                              );
    // the modified image
    UIImage *newImage   = [UIImage imageWithCGImage:newImageRef];

    // cleanup
    free(pixels);
    CGImageRelease(imageRef);
    CGColorSpaceRelease(colorspace);
    CGDataProviderRelease(provider);
    CGImageRelease(newImageRef);

【讨论】:

  • 此示例假设您使用的是具有透明度的 png。如果您不使用透明度,请将 for 循环中的 i += 4 更改为 i += 3(我确信有一种更智能的方法来确定像素大小,但图形编程不是我的强项)。
  • 将像素视为unsigned char (0-255) 可以更轻松地处理它们。
【解决方案3】:

This 可以帮到你。

完成后,您可以使用CGImageCreate 创建一个CGImageRef,然后使用+[UIImage imageWithCGImage:] 创建一个新的UIImage。

【讨论】:

  • 嘿,我已经理解您给出的示例,但是可以在这里发布源代码吗?我也在尝试像提出的问题一样实施。
【解决方案4】:

我使用以下代码在图像的左右添加两条细红线

-(UIImage*)ModifyImage:(UIImage*) img
{
    UIGraphicsBeginImageContext(img.size);

    [img drawInRect:CGRectMake(0,0,img.size.width,img.size.height) blendMode:kCGBlendModeSourceOut alpha:1.0f];

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    int w =img.size.width;
    int cw,ch;

    cw = img.size.width / 35;
    ch = img.size.height / 35;

    unsigned char* data = CGBitmapContextGetData (ctx);

    for(int y = 0 ; y < img.size.height ; y++)
    {
        for(int x = 0 ; x < img.size.width ; x++)
        {
            //int offset = 4*((w * y) + x);

            int offset = (CGBitmapContextGetBytesPerRow(ctx)*y) + (4 * x);

            int blue    =  data[offset];
            int green   = data[offset+1];
            int red     = data[offset+2];
            //int alpha   = data[offset+3];

            if(x <= (cw * 2) || x >= (cw * 35))
            {
                data[offset]    = 0;
                data[offset+1]  = 0;
                data[offset+2]  = 255;
                data[offset+3]  = 255;
            }
        }
    }

    UIImage *rtimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return rtimg;
}

【讨论】:

    【解决方案5】:

    简短的回答是否定的。但是,您说无论如何都必须进行复制,所以为什么不直接获取 NSData 对象并操作其字节。

    来自 UIImage 上的 Apple 文档:

    因为图像对象是不可变的, 他们也不提供直接访问 到他们的底层图像数据。 但是,您可以获得一个 NSData 对象 包含 PNG 或 JPEG 使用图像数据表示 UIImagePNG 表示和 UIImageJPEGRepresentation 函数。

    要以 PNG 格式获取数据,请使用:

    NSData * UIImagePNGRepresentation (
       UIImage *image
    );
    

    对于 JPEG,使用:

    NSData * UIImageJPEGRepresentation (
       UIImage *image,
       CGFloat compressionQuality
    );
    

    【讨论】:

    • 您真的不必在帖子上签名。我们知道您是谁,您的姓名和图片在您的额外签名右侧不到 300 像素。您也完全无法解释为什么他不能直接编辑像素或为什么它首先是只读的。
    • 我想说你刚刚获得了“Kranky Pants 队长”徽章。重新阅读问题。我向你们保证。他没有问“为什么”。 -Matt(
    • Matt 确实回答了为什么它是只读的——他引用了 Apple 文档,特别是说图像被认为是不可变的。鉴于这是一个约束,您想要进行的任何修改都必须在副本上。然而,答案并不正确。在这种情况下,您不会获得原始像素,而是图像的压缩版本,即 PNG/JPG。除非您实现 JPG/PNG 解压缩算法,否则您将无法修改像素数据。
    • 感谢 Itay 的澄清。这很有帮助。我忘记了那部分。你是绝对正确的。此时您将拥有压缩数据,而不是图像数据。非常好的解决方案。我投票给你! ;-)
    • Matt 的摘录对我很有帮助。
    【解决方案6】:

    在 Shaun 的回答中将 NSData *data 行更改为此,它在 iOS7 中完美运行!

     NSData *data        = (NSData *)CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));
    

    【讨论】:

      【解决方案7】:

      @shaun Inman 的帖子是迄今为止我在网上找到的唯一作品!谢谢!但是似乎至少在 IOS3 中,PNG 颜色的保存与您所说的有所不同,这对我有用(仅适用于 PNG!):

      for(int i = 0; i < [data length]; i += 4) {
          pixels[i+0]   = 0; // eg. alpha?
          pixels[i+1]   = 0; // eg. red!
          pixels[i+2]   = 0; // eg. green!
          pixels[i+3]   = pixels[i+3]; // eg. blue!
      }
      

      如果有人能找到一种适用于任何文件类型的方法,那就太棒了!

      【讨论】:

      • 您必须查看CGBitmapInfo 和每个组件的位数以破译其编码方式,然后以编程方式处理每个排列(RGBA、ARGB、CMYK、8bpc、16bpc、整数与浮点组件等) .)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-18
      • 1970-01-01
      • 2013-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多