Получение пиксельных данных из UIImageView работает на симуляторе, а не на устройстве

На основе ответов на предыдущий вопрос, я создал категорию в UIImageView для извлечения данных пикселей. Это нормально работает в симуляторе, но не при развертывании на устройстве. Я бы сказал, не всегда - странно то, что он получает правильный цвет пикселя, если point.x == point.y; в противном случае он дает мне пиксельные данные для пикселя на другой стороне этой линии, как если бы он был зеркальным. (Таким образом, нажатие на пиксель в нижнем правом углу изображения дает мне пиксельные данные для соответствующего пикселя в верхнем левом углу, но нажатие на пиксель в нижнем левом углу возвращает правильный цвет пикселя). Координаты касания (CGPoint) верны.

Что я делаю неправильно?

Вот мой код:

@interface UIImageView (PixelColor)
- (UIColor*)getRGBPixelColorAtPoint:(CGPoint)point;
@end

@implementation UIImageView (PixelColor)

- (UIColor*)getRGBPixelColorAtPoint:(CGPoint)point
{
    UIColor* color = nil;

    CGImageRef cgImage = [self.image CGImage];
    size_t width = CGImageGetWidth(cgImage);
    size_t height = CGImageGetHeight(cgImage);
    NSUInteger x = (NSUInteger)floor(point.x);
    NSUInteger y = height - (NSUInteger)floor(point.y);

    if ((x < width) && (y < height))
    {
        CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
        CFDataRef bitmapData = CGDataProviderCopyData(provider);
        const UInt8* data = CFDataGetBytePtr(bitmapData);
        size_t offset = ((width * y) + x) * 4;
        UInt8 red = data[offset];
        UInt8 blue = data[offset+1];
        UInt8 green = data[offset+2];
        UInt8 alpha = data[offset+3];
        CFRelease(bitmapData);
        color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
    }

    return color;
}

person Shaggy Frog    schedule 15.12.2009    source источник
comment
вы можете проверить значение self.image.imageOrientation? возможно, что изображение, которое вы используете, находится в UIImageOrientationLeftMirrored, которое будет отражением, которое вы видите, хотя я не знаю, почему это будет так только на устройстве ...   -  person David Maymudes    schedule 16.12.2009
comment
Это UIImageOrientationUp как на симуляторе, так и на устройстве. Я не уверен, что левое зеркальное отражение - это правильная интерпретация того, что происходит, потому что отражение (по-видимому) происходит по диагонали y = x, а UIImageOrientationLeftMirrored - это простой поворот всего изображения на 90 градусов против часовой стрелки в соответствии с SDK.   -  person Shaggy Frog    schedule 16.12.2009
comment
Дополнительная информация: если я поменяю местами, как вычисляются x и y, поведение меняется на противоположное - оно работает на устройстве, но не на симуляторе. (Хотя точное позиционирование на устройстве кажется отклоненным на несколько градусов против часовой стрелки, но это может быть неточность моего пальца по сравнению с использованием мыши)   -  person Shaggy Frog    schedule 16.12.2009


Ответы (3)


Я думаю, что R B G ошибается. У вас есть:

UInt8 red =   data[offset];     
UInt8 blue =  data[offset+1];
UInt8 green = data[offset+2];

Но разве вы не имеете в виду R G B? :

UInt8 red =   data[offset];     
UInt8 green = data[offset+1];
UInt8 blue =  data[offset+2];

Но даже после того, как это исправлено, проблема все еще существует, поскольку Apple меняет байты (отличная статья) значения R и B на устройстве, но не на симуляторе.

У меня была аналогичная проблема с симулятором / устройством с пиксельным буфером PNG, возвращаемым CFDataGetBytePtr.

Это решило проблему для меня:

#if TARGET_IPHONE_SIMULATOR
        UInt8 red =   data[offset];
        UInt8 green = data[offset + 1];
        UInt8 blue =  data[offset + 2];
#else
        //on device
        UInt8 blue =  data[offset];       //notice red and blue are swapped
        UInt8 green = data[offset + 1];
        UInt8 red =   data[offset + 2];
#endif

Не уверен, что это решит вашу проблему, но ваш некорректный код очень похож на мой, прежде чем я его исправил.

И последнее: я считаю, что симулятор позволит вам получить доступ к пиксельному буферу data[] даже после вызова CFRelease(bitmapData). По моему опыту, это не на устройстве. Ваш код не должен быть затронут, но, если это поможет кому-то другому, я подумал, что упомянул бы об этом.

person Monte Hurd    schedule 10.06.2010
comment
Мне потребовалось некоторое время, чтобы наконец вернуться к этому, но ваш код был удачным. Комбинация смешения G и B и незнание об изменении порядка байтов, которое Xcode делает за кулисами, была фатальной комбинацией. - person Shaggy Frog; 29.07.2010

Вы можете попробовать следующий альтернативный подход:

  • создать CGBitmapContext
  • нарисовать изображение в контексте
  • вызовите CGBitmapContextGetData в контексте, чтобы получить базовые данные
  • выработайте свое смещение в необработанных данных (в зависимости от того, как вы создали контекст растрового изображения)
  • извлечь значение

Этот подход работает у меня на симуляторе и устройстве.

person Andrew Ebling    schedule 17.12.2009
comment
Я пытаюсь избежать этого из-за накладных расходов (эта функция может вызываться более одного раза в секунду) - person Shaggy Frog; 17.12.2009
comment
Вы можете кэшировать необработанные данные, тогда накладные расходы уменьшаются до поиска в массиве, если изображение не меняется постоянно. - person Andrew Ebling; 17.12.2009
comment
Это правда. Однако я все же хотел бы знать, в чем основная проблема в моем приведенном выше коде. - person Shaggy Frog; 18.12.2009

Похоже, что в коде, опубликованном в исходных вопросах, вместо:

NSUInteger x = (NSUInteger)floor(point.x);
NSUInteger y = height - (NSUInteger)floor(point.y);

Должен быть:

NSUInteger x = (NSUInteger)floor(point.x);
NSUInteger y = (NSUInteger)floor(point.y);
person Gu1234    schedule 26.12.2010