Uiimage (코코아 터치) 또는 cgimage (코어 그래픽)에서 픽셀 데이터를 얻는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/448125

문제

나는 uiimage (코코아 터치)가 있습니다. 그것으로부터, 나는 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];

애플 기술 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는 원하는 픽셀 만 이미지를 변위하여 원하는 픽셀 만 컨텍스트에서 하나의 픽셀과 정렬되도록하여 원하는 픽셀 만 1x1 컨텍스트로 렌더링합니다.

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입니다.

에 따라 uiimage에 대한 Apple 참조 객체는 불변이 아니며 백 바이트에 액세스 할 수 없습니다. 인구가 발생하면 cgimage 데이터에 액세스 할 수 있다는 것은 사실입니다. UIImage a CGImage (명시 적으로 또는 암시 적으로) 돌아올 것입니다 NULL 만약 UIImage a CIImage 그 반대.

이미지 개체는 기본 이미지 데이터에 직접 액세스하지 못합니다. 그러나 앱에서 사용할 다른 형식으로 이미지 데이터를 검색 할 수 있습니다. 특히 CGIMAGE 및 CIIMAGE 속성을 사용하여 각각 코어 그래픽 및 코어 이미지와 호환되는 이미지의 버전을 검색 할 수 있습니다. uiimagepngrepresentation을 사용할 수도 있습니다 (:) 및 uiimagejpegrepresentation (: _ :) PNG 또는 JPEG 형식으로 이미지 데이터를 포함하는 NSDATA 객체를 생성하는 기능.

이 문제를 해결하는 일반적인 트릭

언급 한 바와 같이 옵션이 있습니다

  • uiimagepngrepresentation 또는 jpeg
  • 이미지에 cgimage 또는 ciimage backing data가 있는지 확인하고 거기에 가져 오십시오.

ARGB, PNG 또는 JPEG 데이터가 아닌 출력을 원하고 데이터가 CIIMAGE에 의해 아직 뒷받침되지 않는 경우 이들 중 어느 것도 특히 좋은 트릭이 아닙니다.

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

프로젝트를 개발하는 동안 Uiimage를 완전히 피하고 다른 것을 선택하는 것이 더 합리적 일 수 있습니다. OBJ-C 이미지 래퍼 인 UIIMAGE는 종종 CGIMAGE에 의해 우리가 당연한 것으로 간주되는 지점으로 뒷받침됩니다. ciimage는 당신이 사용할 수 있다는 점에서 더 나은 래퍼 형식 인 경향이 있습니다. 시콘 텍스트 그것이 어떻게 만들어 졌는지 알지 못할 필요없이 원하는 형식을 꺼내려면. 귀하의 경우 비트 맵을 얻는 것은 전화의 문제입니다.

- 렌더 : TobitMap : RowBytes : bounds : Format : Colorspace :

추가 보너스로 이미지에 필터를 체인하여 이미지에 대한 멋진 조작을 시작할 수 있습니다. 이것은 이미지가 거꾸로 있거나 회전/스케일링 해야하는 많은 문제를 해결합니다.

Olie와 Algal의 답변을 바탕으로 Swift 3에 대한 업데이트 된 답변이 있습니다.

public func getRGBAs(fromImage image: UIImage, x: Int, y: Int, count: Int) -> [UIColor] {

var result = [UIColor]()

// First get the image into your data buffer
guard let cgImage = image.cgImage else {
    print("CGContext creation failed")
    return []
}

let width = cgImage.width
let height = cgImage.height
let colorSpace = CGColorSpaceCreateDeviceRGB()
let rawdata = calloc(height*width*4, MemoryLayout<CUnsignedChar>.size)
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
let bitsPerComponent = 8
let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue

guard let context = CGContext(data: rawdata, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
    print("CGContext creation failed")
    return result
}

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

// Now your rawData contains the image data in the RGBA8888 pixel format.
var byteIndex = bytesPerRow * y + bytesPerPixel * x

for _ in 0..<count {
    let alpha = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 3, as: UInt8.self)) / 255.0
    let red = CGFloat(rawdata!.load(fromByteOffset: byteIndex, as: UInt8.self)) / alpha
    let green = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 1, as: UInt8.self)) / alpha
    let blue = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 2, as: UInt8.self)) / alpha
    byteIndex += bytesPerPixel

    let aColor = UIColor(red: red, green: green, blue: blue, alpha: alpha)
    result.append(aColor)
}

free(rawdata)

return result
}

다른 답변을 기반으로하지만 주로 이것, 이것은 내가 필요로하는 것에 효과가 있습니다.

UIImage *image1 = ...; // The image from where you want a pixel data
int pixelX = ...; // The X coordinate of the pixel you want to retrieve
int pixelY = ...; // The Y coordinate of the pixel you want to retrieve

uint32_t pixel1; // Where the pixel data is to be stored
CGContextRef context1 = CGBitmapContextCreate(&pixel1, 1, 1, 8, 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst);
CGContextDrawImage(context1, CGRectMake(-pixelX, -pixelY, CGImageGetWidth(image1.CGImage), CGImageGetHeight(image1.CGImage)), image1.CGImage);
CGContextRelease(context1);

이 라인의 결과로, 당신은 4 바이트 부호없는 정수에서 Alpha가 항상 FF로 설정된 aarrggbb 형식의 픽셀을 갖게됩니다. pixel1.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top