2011-06-29 12 views
9

Tengo una imagen bastante grande, casi a pantalla completa, que voy a mostrar en un iPad. La imagen es aproximadamente 80% transparente. Necesito, en el cliente, determinar el cuadro delimitador de los píxeles opacos y luego recortarlo en ese cuadro delimitador.Recortar UIImager a alpha

Escaneo otras preguntas aquí en StackOverflow y la lectura de algunos de los documentos CoreGraphics, creo que podría lograr esto mediante:

CGBitmapContextCreate(...) // Use this to render the image to a byte array 

.. 
    - iterate through this byte array to find the bounding box 
.. 

CGImageCreateWithImageInRect(image, boundingRect); 

Eso sólo parece muy ineficiente y torpe. ¿Hay algo inteligente que pueda hacer con las máscaras de CGImage o algo que haga uso de la aceleración de gráficos del dispositivo para hacer esto?

+0

¿Has probado esto en un dispositivo? Apuesto a que sería más rápido de lo que piensas. –

+0

Es verdad: una vez que me senté e implementé, el tiempo de procesamiento fue mucho más rápido de lo que pensé. – MikeQ

Respuesta

0

No hay ningún truco inteligente para evitar que el dispositivo haga el trabajo, pero hay algunas maneras de acelerar la tarea o minimizar el impacto en la interfaz de usuario.

Primero, considere la necesidad de acelerar esta tarea. Una iteración simple a través de esta matriz de bytes puede ir lo suficientemente rápido. Es posible que no haya necesidad de invertir en la optimización de esta tarea si la aplicación solo calcula esto una vez por ejecución o como reacción a la elección del usuario que toma al menos unos segundos entre las elecciones.

Si el cuadro delimitador no es necesario durante un tiempo después de que la imagen esté disponible, esta iteración se puede iniciar en un hilo separado. De esta forma, el cálculo no bloquea el hilo de la interfaz principal. Grand Central Dispatch puede facilitar el uso de un hilo separado para esta tarea.

Si la tarea debe acelerarse, tal vez este es el procesamiento en tiempo real de las imágenes de video, entonces el procesamiento en paralelo de los datos puede ayudar. El marco Accelerate puede ayudar a configurar los cálculos SIMD en los datos. O bien, para obtener realmente el rendimiento con esta iteración, el código de lenguaje de ensamblaje ARM que utiliza las operaciones NEON SIMD podría obtener excelentes resultados con un esfuerzo de desarrollo significativo.

La última opción es investigar un mejor algoritmo. Hay un gran cuerpo de trabajo para detectar características en imágenes. Un algoritmo de detección de bordes puede ser más rápido que una iteración simple a través de la matriz de bytes. Tal vez Apple agregará capacidades de detección de bordes a Core Graphics en el futuro que se pueden aplicar a este caso. Una capacidad de procesamiento de imágenes implementada por Apple puede no ser una coincidencia exacta para este caso, pero la implementación de Apple debe optimizarse para usar las capacidades SIMD o GPU del iPad, lo que resulta en un mejor rendimiento general.

8

creé una categoría en uImage que hace esto si alguien lo necesita ...

+ (UIImage *)cropTransparencyFromImage:(UIImage *)img { 

    CGImageRef inImage = img.CGImage;   
    CFDataRef m_DataRef; 
    m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage)); 
    UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef); 

    int width = img.size.width; 
    int height = img.size.height; 

    CGPoint top,left,right,bottom; 

    BOOL breakOut = NO; 
    for (int x = 0;breakOut==NO && x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       left = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 
     } 
    } 

    breakOut = NO; 
    for (int y = 0;breakOut==NO && y < height; y++) { 

     for (int x = 0; x < width; x++) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       top = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int y = height-1;breakOut==NO && y >= 0; y--) { 

     for (int x = width-1; x >= 0; x--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       bottom = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int x = width-1;breakOut==NO && x >= 0; x--) { 

     for (int y = height-1; y >= 0; y--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       right = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 


    CGRect cropRect = CGRectMake(left.x, top.y, right.x - left.x, bottom.y - top.y); 

    UIGraphicsBeginImageContextWithOptions(cropRect.size, 
              NO, 
              0.); 
    [img drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y) 
      blendMode:kCGBlendModeCopy 
       alpha:1.]; 
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return croppedImage; 
} 
+1

Gracias por este script. Lo uso en mi proyecto, pero noto un problema de pérdida de memoria. Creo que debemos llamar a "CFRelease (m_DataRef);" antes de "return croppedImage"; –

+0

Gracias por su útil respuesta user404. ¿Sabes si hay una manera, después de haber recuperado los puntos para aplicarles una transformación? –

+0

Solo repito mi comentario en caso de que otros estén mirando: Acabo de implementar esto en mi programa, ¡es exactamente lo que necesito! Pero obtengo y EXC_BAD_ACCESS en la primera si (m_PixelBuf [loc + 3]! = 0) {línea. ¿Alguna idea de por qué?Gracias – Smikey

13

Gracias a user404709 para hacer todo el trabajo duro. Debajo del código también maneja imágenes de retina y libera el CFDataRef.

- (UIImage *)trimmedImage { 

    CGImageRef inImage = self.CGImage; 
    CFDataRef m_DataRef; 
    m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage)); 

    UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef); 

    size_t width = CGImageGetWidth(inImage); 
    size_t height = CGImageGetHeight(inImage); 

    CGPoint top,left,right,bottom; 

    BOOL breakOut = NO; 
    for (int x = 0;breakOut==NO && x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       left = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 
     } 
    } 

    breakOut = NO; 
    for (int y = 0;breakOut==NO && y < height; y++) { 

     for (int x = 0; x < width; x++) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       top = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int y = height-1;breakOut==NO && y >= 0; y--) { 

     for (int x = width-1; x >= 0; x--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       bottom = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int x = width-1;breakOut==NO && x >= 0; x--) { 

     for (int y = height-1; y >= 0; y--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       right = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 


    CGFloat scale = self.scale; 

    CGRect cropRect = CGRectMake(left.x/scale, top.y/scale, (right.x - left.x)/scale, (bottom.y - top.y)/scale); 
    UIGraphicsBeginImageContextWithOptions(cropRect.size, 
              NO, 
              scale); 
    [self drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y) 
      blendMode:kCGBlendModeCopy 
       alpha:1.]; 
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    CFRelease(m_DataRef); 
    return croppedImage; 
} 
+0

Acabo de implementar esto en mi programa, ¡es exactamente lo que necesito! Pero me sale y EXC_BAD_ACCESS en la primera línea de if (m_PixelBuf [loc + 3]! = 0) { . ¿Alguna idea de por qué? Gracias. – Smikey

+0

@Smikey, ¿están intentando usar esto en una imagen capturada desde la cámara? Si es así, de acuerdo con esto [http://stackoverflow.com/a/12614015/1541893] por el usuario1702121, "' CGBitmapContextGetData() 'en el contexto creado a partir de un búfer de muestra capturado no lo devuelve bitmap". Él proporciona una solución. – Dan