[cocoa] UIImage (Cocoa Touch) 또는 CGImage (Core Graphics)에서 픽셀 데이터를 얻는 방법은 무엇입니까?

UIImage (Cocoa Touch)가 있습니다. 그로부터 CGImage 또는 기타 원하는 것을 얻을 수있어서 기쁩니다. 이 함수를 작성하고 싶습니다 :

- (int)getRGBAFromImage:(UIImage *)image atX:(int)xx andY:(int)yy {
  // [...]
  // What do I want to read about to help
  // me fill in this bit, here?
  // [...]

  int result = (red << 24) | (green << 16) | (blue << 8) | alpha;
  return result;
}



답변

참고로 Keremk의 답변을 원래의 개요와 결합하고 오타를 정리하고 여러 색상을 반환하도록 일반화하고 모든 것을 컴파일했습니다. 결과는 다음과 같습니다.

+ (NSArray*)getRGBAsFromImage:(UIImage*)image atX:(int)x andY:(int)y count:(int)count
{
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];

    // First get the image into your data buffer
    CGImageRef imageRef = [image CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                    bitsPerComponent, bytesPerRow, colorSpace,
                    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);

    // Now your rawData contains the image data in the RGBA8888 pixel format.
    NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
    for (int i = 0 ; i < count ; ++i)
    {
        CGFloat alpha = ((CGFloat) rawData[byteIndex + 3] ) / 255.0f;
        CGFloat red   = ((CGFloat) rawData[byteIndex]     ) / alpha;
        CGFloat green = ((CGFloat) rawData[byteIndex + 1] ) / alpha;
        CGFloat blue  = ((CGFloat) rawData[byteIndex + 2] ) / alpha;
        byteIndex += bytesPerPixel;

        UIColor *acolor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
        [result addObject:acolor];
    }

  free(rawData);

  return result;
}


답변

이를 수행하는 한 가지 방법은 이미지를 주어진 색상 공간 (이 경우 RGB)에 대해 주어진 버퍼가 지원하는 비트 맵 컨텍스트로 그리는 것입니다. 픽셀 값을 얻을 때 마다이 작업을 수행하는 대신 캐시하고 싶습니다.)

샘플로 아래를 참조하십시오.

// First get the image into your data buffer
CGImageRef image = [myUIImage CGImage];
NSUInteger width = CGImageGetWidth(image);
NSUInteger height = CGImageGetHeight(image);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);

CGContextDrawImage(context, CGRectMake(0, 0, width, height));
CGContextRelease(context);

// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;
red = rawData[byteIndex];
green = rawData[byteIndex + 1];
blue = rawData[byteIndex + 2];
alpha = rawData[byteIndex + 3];


답변

Apple의 기술 Q & A QA1509 는 다음과 같은 간단한 접근 방식을 보여줍니다.

CFDataRef CopyImagePixels(CGImageRef inImage)
{
    return CGDataProviderCopyData(CGImageGetDataProvider(inImage));
}

CFDataGetBytePtr실제 바이트 (및 CGImageGet*해석 방법을 이해하기위한 다양한 방법) 를 얻는 데 사용하십시오 .


답변

여기에 하나의 정답없다는 것을 믿을 수 없었 습니다. 포인터를 할당 할 필요가 없으며 곱하지 않은 값은 여전히 ​​정규화해야합니다. 추격을 자르려면 Swift 4의 올바른 버전이 UIImage있습니다 .cgImage.

extension CGImage {
    func colors(at: [CGPoint]) -> [UIColor]? {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bytesPerPixel = 4
        let bytesPerRow = bytesPerPixel * width
        let bitsPerComponent = 8
        let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue

        guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo),
            let ptr = context.data?.assumingMemoryBound(to: UInt8.self) else {
            return nil
        }

        context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))

        return at.map { p in
            let i = bytesPerRow * Int(p.y) + bytesPerPixel * Int(p.x)

            let a = CGFloat(ptr[i + 3]) / 255.0
            let r = (CGFloat(ptr[i]) / a) / 255.0
            let g = (CGFloat(ptr[i + 1]) / a) / 255.0
            let b = (CGFloat(ptr[i + 2]) / a) / 255.0

            return UIColor(red: r, green: g, blue: b, alpha: a)
        }
    }
}

이미지를 먼저 버퍼로 그리거나 변환해야하는 이유는 이미지가 여러 가지 형식을 가질 수 있기 때문입니다. 이 단계는 읽을 수있는 일관된 형식으로 변환하는 데 필요합니다.


답변

다음은 @Matt가 원하는 픽셀이 컨텍스트의 한 픽셀과 정렬되도록 이미지를 대체하여 원하는 픽셀 만 1×1 컨텍스트로 렌더링 하는 SO 스레드 입니다.


답변

NSString * path = [[NSBundle mainBundle] pathForResource:@"filename" ofType:@"jpg"];
UIImage * img = [[UIImage alloc]initWithContentsOfFile:path];
CGImageRef image = [img CGImage];
CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(image));
const unsigned char * buffer =  CFDataGetBytePtr(data);


답변

UIImage는 바이트가 CGImage 또는 CIImage 인 랩퍼입니다.

UIImageApple Reference에 따르면 객체는 변경할 수 없으며 백업 바이트에 액세스 할 수 없습니다. 를 (명시 적으로 또는 암시 적으로) 채운 경우 CGImage 데이터에 액세스 할 수있는 것이 사실이지만, UIImagea 로 뒷받침 되면 그 반대로 CGImage반환 NULL됩니다 .UIImageCIImage

이미지 객체는 기본 이미지 데이터에 직접 액세스 할 수 없습니다. 그러나 앱에서 사용하기 위해 다른 형식으로 이미지 데이터를 검색 할 수 있습니다. 특히 cgImage 및 ciImage 속성을 사용하여 각각 Core Graphics 및 Core Image와 호환되는 이미지 버전을 검색 할 수 있습니다. UIImagePNGRepresentation ( 🙂 및 UIImageJPEGRepresentation ( : _ 🙂 함수를 사용하여 PNG 또는 JPEG 형식의 이미지 데이터를 포함하는 NSData 객체를 생성 할 수도 있습니다 .

이 문제를 해결하는 일반적인 방법

명시된 바와 같이 귀하의 옵션은

  • UIImagePNGRepresentation 또는 JPEG
  • 이미지에 CGImage 또는 CIImage 백업 데이터가 있는지 확인하고이를 가져옵니다.

ARGB, PNG 또는 JPEG 데이터가 아닌 출력을 원하고 데이터가 이미 CIImage에 의해 백업되지 않은 경우에는 이들 중 어느 것도 특히 유용한 트릭이 아닙니다.

내 추천, CIImage를 사용해보십시오

프로젝트를 개발하는 동안 UIImage를 완전히 피하고 다른 것을 선택하는 것이 더 합리적 일 수 있습니다. Obj-C 이미지 래퍼 인 UIImage는 종종 CGImage에 의해 당연한 것으로 간주됩니다. CIImage는 CIContext 를 사용하여 원하는 형식을 만들지 않고도 원하는 형식을 얻을 수 있다는 점에서 더 나은 래퍼 형식입니다 . 귀하의 경우 비트 맵을 얻는 것은 전화의 문제입니다

-렌더 : toBitmap : rowBytes : bounds : format : colorSpace :

추가 보너스로 필터를 이미지에 연결하여 이미지를 멋지게 조작 할 수 있습니다. 이렇게하면 이미지가 거꾸로되어 있거나 회전 / 크기 조정되어야하는 많은 문제가 해결됩니다.