Могу ли я редактировать пиксели свойства UIImage CGImage

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 вызываются только один раз.

7 ответов

Решение

Это может помочь вам.

Когда вы закончите, вы можете использовать CGImageCreate создать CGImageRef, затем использовать +[UIImage imageWithCGImage:] создать новый UIImage.

Для более тупых среди нас (читай: будущий я) вот рабочий код, основанный на ответах Итай и Дейва Р. Он начинается с 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/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

Я использовал следующий код, чтобы добавить 2 тонкие красные линии справа и слева от изображения

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

Краткий ответ: нет. Тем не менее, вы говорите, что все равно должны сделать копию, так почему бы просто не получить объект NSData и манипулировать его байтами.

Из документов Apple на UIImage:

Поскольку объекты изображений являются неизменяемыми, они также не обеспечивают прямой доступ к данным, лежащим в их основе. Однако вы можете получить объект NSData, содержащий представление данных изображения в формате PNG или JPEG, используя функции UIImagePNGRepresentation и UIImageJPEGRepresentation.

Чтобы получить данные в формате PNG, используйте:

NSData * UIImagePNGRepresentation (
   UIImage *image
);

для JPEG используйте:

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

Измените строку данных NSData *, в ответе Шона, на эту, и она отлично работает в iOS7!

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

@shaun Сообщение Инмана было единственной работой, которую я нашел в Интернете до сих пор! БЛАГОДАРЮ ВАС! Однако, похоже, что, по крайней мере, в 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!
}

Было бы здорово, если бы кто-нибудь смог найти способ, который бы работал для любого типа файла!

Другие вопросы по тегам