2009-06-25 35 views
35

Tôi hiện đang cố gắng lấy giá trị alpha của pixel trong UIImageView. Tôi đã thu được CGImage từ [hình ảnh UIImageView] và tạo ra một mảng byte RGBA từ này. Alpha là thủ phạm.Lấy giá trị alpha pixel cho UIImage

CGImageRef image = uiImage.CGImage; 
NSUInteger width = CGImageGetWidth(image); 
NSUInteger height = CGImageGetHeight(image); 
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
rawData = malloc(height * width * 4); 
bytesPerPixel = 4; 
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), image); 
CGContextRelease(context); 

Sau đó tôi tính toán chỉ mục mảng cho kênh alpha đã cho bằng cách sử dụng toạ độ từ UIImageView.

int byteIndex = (bytesPerRow * uiViewPoint.y) + uiViewPoint.x * bytesPerPixel; 
unsigned char alpha = rawData[byteIndex + 3]; 

Tuy nhiên, tôi không nhận được các giá trị mà tôi mong đợi. Đối với một khu vực trong suốt hoàn toàn màu đen của hình ảnh tôi nhận được các giá trị khác 0 cho kênh alpha. Tôi có cần dịch các tọa độ giữa UIKit và Đồ họa cốt lõi - tức là: trục y đảo ngược không? Hay tôi đã hiểu lầm các giá trị alpha được ghi nhận trước?

Cập nhật:

gợi ý @Nikolai Ruhe là chìa khóa để này. Tôi không thực sự cần phải dịch giữa các tọa độ UIKit và tọa độ Đồ họa cốt lõi. Tuy nhiên, sau khi đặt chế độ hòa trộn, các giá trị alpha của tôi là những gì tôi mong đợi:

CGContextSetBlendMode(context, kCGBlendModeCopy); 
+0

Hey teabot - Tôi nhận ra câu hỏi này đã được trả lời, nhưng tôi đã làm một cái gì đó tương tự như thế này một vài ngày trước. Thay vì vẽ toàn bộ hình ảnh và sau đó lập chỉ mục vào mảng byte, bạn nên _only vẽ 1px của hình ảnh nguồn thành ngữ cảnh bitmap 1px x 1px. Bạn có thể sử dụng tham số rect trong CGContextDrawImage để đặt đúng pixel vào, và nó nhanh hơn 1000 lần :-) –

+0

Cảm ơn nhận xét @Ben Gotow - Tôi chỉ vẽ hình ảnh một lần và sau đó tiếp tục sử dụng cùng một mảng byte. @Nikolai Ruhe cũng đề nghị vẽ điểm ảnh đơn lẻ nhưng cho rằng cách tiếp cận mảng của tôi sẽ nhanh hơn nếu tôi không cần vẽ hình ảnh nhiều lần, nhưng cần phải tra cứu alpha liên tục. – teabot

+1

Chỉ cần cập nhật nhanh (năm sau) sau khi sử dụng câu hỏi này và một câu hỏi khác trên đây cho một vấn đề tương tự: Tham số rect trên CGContextDrawImage được sử dụng để điều khiển "Vị trí và kích thước trong không gian người dùng của hộp giới hạn để vẽ hình ảnh. " Vì vậy, nếu bạn thực hiện kích thước 1x1 trực tiếp đó, nó sẽ phóng to toàn bộ hình ảnh xuống 1x1 trước khi vẽ. Để có được pixel đúng, bạn cần sử dụng CGContextTranslateCTM như trong câu trả lời của Nikolai, và để kích thước của rect bằng với hình ảnh nguồn để ngăn chặn sự co giãn. – roguenet

Trả lời

10

Có, CGContts có trục y hướng lên trong khi ở UIKit nó chỉ xuống. See the docs.

Chỉnh sửa sau khi đọc mã:

Bạn cũng muốn thiết lập chế độ hòa trộn để thay thế trước khi vẽ các hình ảnh kể từ khi bạn muốn giá trị alpha của hình ảnh, chứ không phải một mà là trong bộ đệm của bối cảnh trước:

CGContextSetBlendMode(context, kCGBlendModeCopy); 

chỉnh sửa sau khi suy nghĩ:

Bạn có thể thực hiện tra cứu nhiều hiệu quả hơn bằng cách xây dựng CGBitmapContext nhỏ nhất có thể (1x1 pixel? có lẽ 8x8? có một thử) và dịch bối cảnh đến vị trí mong muốn của bạn trước khi vẽ:

CGContextTranslateCTM(context, xOffset, yOffset); 
+0

@Nikolai Ruhe - Cuộc gọi tốt - Tôi nên đọc tài liệu. – teabot

+0

CGImageRef đại diện cho một hình ảnh tĩnh. Một khi tôi có mảng byte tôi ném CGImage đi và sau đó liên tục sử dụng mảng byte để thực hiện tra cứu alpha. Tối ưu hóa của bạn có hoạt động trong trường hợp này không? – teabot

+0

Không, tất nhiên bạn sẽ cần hình ảnh để vẽ liên tục.Cách tiếp cận của tôi có thể hiệu quả hơn đối với vài lần tra cứu. Nếu bạn đang thực hiện rất nhiều tra cứu, hãy gắn bó với mã của bạn. –

3

Tôi có cần phải dịch các tọa độ giữa UIKit và Core Graphics - tức là: là trục y đảo ngược?

Có thể. Trong CGImage, dữ liệu pixel theo thứ tự đọc tiếng Anh: từ trái sang phải, từ trên xuống dưới. Vì vậy, pixel đầu tiên trong mảng là trên cùng bên trái; pixel thứ hai là một từ bên trái trên hàng đầu; v.v.

Giả sử bạn có quyền đó, bạn cũng nên đảm bảo bạn đang xem đúng thành phần trong pixel. Có lẽ bạn đang mong đợi RGBA nhưng yêu cầu ARGB, hoặc ngược lại. Hoặc, có thể bạn có thứ tự byte sai (Tôi không biết tính cuối cùng của iPhone là gì).

Hoặc tôi đã hiểu nhầm các giá trị alpha ưu tiên không?

Nó không giống như vậy.

Đối với những người không biết: Premultiplied có nghĩa là các thành phần màu sắc được preipliplied bởi alpha; thành phần alpha là giống nhau cho dù các thành phần màu sắc có được ban đầu bởi nó hay không. Bạn có thể đảo ngược điều này (không phân biệt) bằng cách chia các thành phần màu theo alpha.

+0

@Peter Hosey - Cảm ơn bạn đã làm rõ cách thức hoạt động của công nghệ alpha cấp tiến - tôi đã không chắc chắn. – teabot

3

Tôi tìm thấy câu hỏi/câu trả lời này trong khi nghiên cứu cách phát hiện va chạm giữa các sprites sử dụng giá trị alpha của dữ liệu hình ảnh, chứ không phải là hộp giới hạn hình chữ nhật. Bối cảnh là một ứng dụng iPhone ... Tôi đang cố gắng thực hiện việc vẽ 1 pixel ở trên và tôi vẫn gặp sự cố khi thực hiện việc này, nhưng tôi đã tìm thấy cách dễ dàng hơn để tạo CGContextRef bằng dữ liệu từ chính hình ảnh đó và chức năng trợ giúp tại đây:

CGContextRef context = CGBitmapContextCreate(
       rawData, 
       CGImageGetWidth(cgiRef), 
       CGImageGetHeight(cgiRef), 
       CGImageGetBitsPerComponent(cgiRef), 
       CGImageGetBytesPerRow(cgiRef), 
       CGImageGetColorSpace(cgiRef), 
       kCGImageAlphaPremultipliedLast  
    ); 

Điều này bỏ qua tất cả các mã cứng xấu xí trong ví dụ trên. Giá trị cuối cùng có thể được lấy ra bằng cách gọi CGImageGetBitmapInfo() nhưng trong trường hợp của tôi, nó trả về một giá trị từ hình ảnh gây ra lỗi trong hàm ContextCreate. Chỉ một số kết hợp nhất định có giá trị như được ghi ở đây: http://developer.apple.com/qa/qa2001/qa1037.html

Hy vọng điều này hữu ích!

+2

Điều này hữu ích. Chỉ cần ghi nhớ, rằng ở đây, BytesPerRow của bạn có thể không được chiều rộng * bytesPerPixel. Để tối ưu hóa, nó có thể được đệm đến 16 byte ranh giới. Khi bạn duyệt qua rawData, nếu bạn không tính đến điều này, bạn sẽ sử dụng byte đệm làm dữ liệu pixel. – mahboudz

+0

Điều đó cũng có nghĩa là rawData của bạn mà bạn malloced có thể là nhỏ để giữ bitmap và có thể có một bộ đệm overrun. – mahboudz

+1

Tôi tự hỏi nếu đây là lý do tại sao tôi gặp sự cố khi làm việc này trên iPhone, nhưng nó hoạt động tốt trên trình mô phỏng? http://stackoverflow.com/questions/7506248/alpha-detection-in-layer-ok-on-simulator-not-iphone –

36

Nếu tất cả những gì bạn muốn là giá trị alpha của một điểm duy nhất, tất cả những gì bạn cần là bộ đệm một điểm alpha. Tôi tin rằng điều này là đủ:

// assume im is a UIImage, point is the CGPoint to test 
CGImageRef cgim = im.CGImage; 
unsigned char pixel[1] = {0}; 
CGContextRef context = CGBitmapContextCreate(pixel, 
             1, 1, 8, 1, NULL, 
             kCGImageAlphaOnly); 
CGContextDrawImage(context, CGRectMake(-point.x, 
            -point.y, 
            CGImageGetWidth(cgim), 
            CGImageGetHeight(cgim)), 
       cgim); 
CGContextRelease(context); 
CGFloat alpha = pixel[0]/255.0; 
BOOL transparent = alpha < 0.01; 

Nếu UIImage không phải được tạo lại mỗi lần, điều này rất hiệu quả.

EDIT 8 tháng 12 năm 2011:

Một người nhận xét chỉ ra rằng trong một số trường hợp, hình ảnh có thể bị lộn xộn. Tôi đã suy nghĩ về điều này, và tôi hơi tiếc rằng tôi đã không viết mã bằng cách sử dụng trực tiếp UIImage, như thế này (tôi nghĩ lý do là tại thời điểm tôi không hiểu về UIGraphicsPushContext):

// assume im is a UIImage, point is the CGPoint to test 
unsigned char pixel[1] = {0}; 
CGContextRef context = CGBitmapContextCreate(pixel, 
              1, 1, 8, 1, NULL, 
              kCGImageAlphaOnly); 
UIGraphicsPushContext(context); 
[im drawAtPoint:CGPointMake(-point.x, -point.y)]; 
UIGraphicsPopContext(); 
CGContextRelease(context); 
CGFloat alpha = pixel[0]/255.0; 
BOOL transparent = alpha < 0.01; 

Tôi nghĩ điều đó sẽ giải quyết được vấn đề lật.

+0

hoạt động như một sự quyến rũ đối với tôi! –

+3

Vì lý do nào đó, tôi phải sử dụng dòng sau cho 'CGContextDrawImage (...': 'CGContextDrawImage (ngữ cảnh, CGRectMake (-point.x, - (image.size.height-point.y), CGImageGetWidth (cgim) CGGageGetHeight (cgim)), cgim); ' –

+0

@MattLeff - Điều đó có ý nghĩa hoàn hảo nếu ảnh của bạn bị lộn xộn, có thể dễ dàng xảy ra do sự không phù hợp trở kháng giữa Đồ họa lõi (nơi xuất xứ y ở dưới) và UIKit (trong đó y nguồn gốc là ở đầu trang) – matt

Các vấn đề liên quan