Невозможно правильно прочитать цветовые данные UIImage, созданные из UIView

Что должно произойти:

Пользователь рисует (например, с помощью Apple Pencil) в UIView. Когда он закончит, из этого представления создается UIImage, данные ARGB считываются один за другим и, наконец, сохраняются в NSData.

Позднее NSData снова используется для воссоздания образа. (Я знаю, что могу добиться чего-то подобного гораздо проще, но, пожалуйста, предположим, что для такого подхода есть веская причина.)

Что происходит на самом деле:

Пользователь рисует в представлении, UIImage берется, но когда код получает доступ к данным изображения, происходит что-то связанное: кажется, что данные не соответствуют изображению, что становится очевидным, когда они используются для реконструкции изображения.

И вот код.

Из UIView

Инициализация

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.layer.allowsEdgeAntialiasing=YES;
        self.backgroundColor=[UIColor clearColor];
    }
    return self;
}

Рисунок

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [[event allTouches]anyObject];
    CGPoint touchLocation = [touch locationInView:self];
    if (myImage!=nil) {
        UIImageView * iV = [[UIImageView alloc] initWithFrame:self.bounds];
        iV.image = myImage;
        [self insertSubview:iV atIndex:0];

    }
    myPath = [UIBezierPath bezierPath];
    [myPath moveToPoint:touchLocation];
}

- (void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [[event allTouches]anyObject];
    CGPoint touchLocation = [touch locationInView:self];
    [myPath addLineToPoint:touchLocation];
    [self setNeedsDisplay];
}

-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        myImage = self.imageRepresentation;
}


- (void) drawRect:(CGRect)rect {

    self.context = UIGraphicsGetCurrentContext();
    [[UIColor blackColor]setStroke];
    myPath.lineWidth=3.0;
    [myPath stroke];

}

Получение UIImage из UIView

-(UIImage *)imageRepresentation{

    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 1.0);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    myImage= UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return myImage;

}

Чтение и преобразование данных изображения

-(NSData *) pixel{

    int sidelength = (int) self.myImage.size.width;
    int numberData = sidelength * sidelength * 4;

    CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(self.myImage.CGImage));
    const UInt8 * data = CFDataGetBytePtr(pixelData);

    UInt8 pixel[numberData];
    for (int y=0; y<sidelength; y++) {
        for (int x=0; x<sidelength; x++) {
            int components = sidelength * y + x * 4;
            UInt8 a = data[components];
            UInt8 r = data[components+1];
            UInt8 g = data[components+2];
            UInt8 b = data[components+3];

            pixel[components]=a;
            pixel[components+1]=r;
            pixel[components+2]=g;
            pixel[components+3]=b;

        }
    }
    CFRelease(pixelData);
    return [NSData dataWithBytes:pixel length:sidelength*sidelength*4];;

Из другого класса

Получение изображения с помощью NSData

-(UIImage *) getImage{

    UInt8 * pixel=malloc(sizeof(UInt8)*self.numberBytes);
    pixel = (UInt8 *) booleanData.bytes;

    const int Width = self.size.width;
    const int Height = self.size.height;
    const int ComponentsPerPixel = 4;

    const size_t BitsPerComponent = 8;
    const size_t BytesPerRow=((BitsPerComponent * Width) / 8) * ComponentsPerPixel;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef gtx = CGBitmapContextCreate(&pixel[0], Width, Height, BitsPerComponent, BytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);

    CGImageRef toCGImage = CGBitmapContextCreateImage(gtx);
    UIImage * image = [[UIImage alloc] initWithCGImage:toCGImage];
    return image;


}

Вот пример результатов:

Исходный рисунок, визуализированный с использованием «imageRepresentation»:

Невозможно правильно прочитать цветовые данные UIImage, созданные из UIView

Полученное изображение с использованием «пикселя» и «getImage»:

Невозможно правильно прочитать цветовые данные UIImage, созданные из UIView

Я ожидаю, что виновником будет метод «пикселей», потому что я проверил его результаты и обнаружил, что они уже довольно неверны. Кроме того, я протестировал «getImage» в других настройках, и он работал нормально. Но странно то, что это верно и для «пикселей». Так возможно ли, что UIView отображается иначе, чем, скажем, png или jpg? Я проверил документацию для UIGraphicsBeginImageContextWithOptions, но это не дало мне полезной подсказки.

Вы предполагаете, что ваше изображение представляет собой квадрат - его высота равна ширине. Вы уверены, что это предположение верно? Еще одна вещь:

Max Pevsner 10.04.2019 17:10

Спасибо. Да, я инициализирую представление (как LetterView, подкласс UIView) из родительского представления следующим образом: self.letterView = [[LetterView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.width )];

magnus1969 10.04.2019 21:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
42
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Есть две проблемы. Во-первых, вы не вставляете все данные изображения в массив pixel в методе pixel.

Если ваше изображение 100x100, то общее количество байтов равно 10000, умноженному на 4. В вашем цикле for самый большой пиксель, который вы получите, будет 100x100+100x4, что составляет 10400. Остальные пиксели содержат мусор.

Этот цикл выполнит задание:

for (unsigned long i = 0; i < numberData; i += 4) {
    pixel[i] = data[i];
    pixel[i + 1] = data[i + 1];
    pixel[i + 2] = data[i + 2];
    pixel[i + 3] = data[i + 3];
}

Вторая проблема интереснее. Воссозданное изображение будет искажено, если его ширина не будет умножена на 64. В CGBitmapContextCreate есть какая-то ошибка - я отправил эту ошибку в Apple около шести лет назад, и они заявили, что исправили ее, но ее можно легко воспроизвести при определенных условиях. условия.

Я запустил ваш код (с новым циклом), и он отлично работает с UIView размером 128x128, но возвращает искаженное изображение, когда размер 100x100.

Вау, первая часть граничит с позором. Думаю, мне придется брать уроки счета. Большое спасибо! Между прочим, в процессе я добавил некоторый код, который преобразует изображение в png, и, кажется, не сталкивался со второй проблемой. Но в любом случае, если я это сделаю, я теперь знаю, что делать.

magnus1969 11.04.2019 21:30

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