检测具有透明背景的 UIImage 的裁剪矩形

Posted

技术标签:

【中文标题】检测具有透明背景的 UIImage 的裁剪矩形【英文标题】:Detect cropping rectangle for UIImage with transparent background 【发布时间】:2013-05-15 15:32:35 【问题描述】:

采用透明背景的 UIImage 并确定要裁剪到的最小矩形以便只留下可见图像数据的策略是什么(当然,如果图像数据不是矩形,则还有额外的透明背景)?

我发现了很多关于将 UIImage 裁剪为 CGRect 的信息,大量需要用户干预的裁剪视图控制器,以及几个具有图像处理类和类别的开源库(包括 MGImageUtilities 和 NYXImagesKit),但还没有解决的问题我的特殊问题。

我当前的应用程序面向 ios 5.0,因此与它的兼容性将是最佳的。

编辑:顺便说一句,我希望在寻找边缘边界的行和列中的图像数据的最坏情况下,有一种比蛮力查看每个像素更好的方法。

【问题讨论】:

【参考方案1】:

你有机会见到https://gist.github.com/spinogrizz/3549921吗?

看起来正是你需要的。

为了不丢失,从该页面复制和粘贴:

- (UIImage *) imageByTrimmingTransparentPixels 
    int rows = self.size.height;
    int cols = self.size.width;
    int bytesPerRow = cols*sizeof(uint8_t);

    if ( rows < 2 || cols < 2 ) 
        return self;
    

    //allocate array to hold alpha channel
    uint8_t *bitmapData = calloc(rows*cols, sizeof(uint8_t));

    //create alpha-only bitmap context
    CGContextRef contextRef = CGBitmapContextCreate(bitmapData, cols, rows, 8, bytesPerRow, NULL, kCGImageAlphaOnly);

    //draw our image on that context
    CGImageRef cgImage = self.CGImage;
    CGRect rect = CGRectMake(0, 0, cols, rows);
    CGContextDrawImage(contextRef, rect, cgImage);

    //summ all non-transparent pixels in every row and every column
    uint16_t *rowSum = calloc(rows, sizeof(uint16_t));
    uint16_t *colSum = calloc(cols, sizeof(uint16_t));

    //enumerate through all pixels
    for ( int row = 0; row < rows; row++) 
        for ( int col = 0; col < cols; col++)
        
            if ( bitmapData[row*bytesPerRow + col] )  //found non-transparent pixel
                rowSum[row]++;
                colSum[col]++;
            
        
    

    //initialize crop insets and enumerate cols/rows arrays until we find non-empty columns or row
    UIEdgeInsets crop = UIEdgeInsetsMake(0, 0, 0, 0);

    for ( int i = 0; i<rows; i++ )         //top
        if ( rowSum[i] > 0 ) 
            crop.top = i; break;
        
    

    for ( int i = rows; i >= 0; i-- )      //bottom
        if ( rowSum[i] > 0 ) 
            crop.bottom = MAX(0, rows-i-1); break;
        
    

    for ( int i = 0; i<cols; i++ )         //left
        if ( colSum[i] > 0 ) 
            crop.left = i; break;
        
    

    for ( int i = cols; i >= 0; i-- )      //right
        if ( colSum[i] > 0 ) 
            crop.right = MAX(0, cols-i-1); break;
        
    

    free(bitmapData);
    free(colSum);
    free(rowSum);

    if ( crop.top == 0 && crop.bottom == 0 && crop.left == 0 && crop.right == 0 ) 
        //no cropping needed
        return self;
    
    else 
        //calculate new crop bounds
        rect.origin.x += crop.left;
        rect.origin.y += crop.top;
        rect.size.width -= crop.left + crop.right;
        rect.size.height -= crop.top + crop.bottom;

        //crop it
        CGImageRef newImage = CGImageCreateWithImageInRect(cgImage, rect);

        //convert back to UIImage
        return [UIImage imageWithCGImage:newImage];
    

【讨论】:

不适合我——但希望有另一个版本在浮动......【参考方案2】:

它在 Swift 4 中

    extension UIImage 

    func cropAlpha() -> UIImage 

        let cgImage = self.cgImage!;

        let width = cgImage.width
        let height = cgImage.height

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bytesPerPixel:Int = 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 self
        

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

        var minX = width
        var minY = height
        var maxX: Int = 0
        var maxY: Int = 0

        for x in 1 ..< width 
            for y in 1 ..< height 

                let i = bytesPerRow * Int(y) + bytesPerPixel * Int(x)
                let a = CGFloat(ptr[i + 3]) / 255.0

                if(a>0) 
                    if (x < minX)  minX = x ;
                    if (x > maxX)  maxX = x ;
                    if (y < minY)  minY = y;
                    if (y > maxY)  maxY = y;
                
            
        

        let rect = CGRect(x: CGFloat(minX),y: CGFloat(minY), width: CGFloat(maxX-minX), height: CGFloat(maxY-minY))
        let imageScale:CGFloat = self.scale
        let croppedImage =  self.cgImage!.cropping(to: rect)!
        let ret = UIImage(cgImage: croppedImage, scale: imageScale, orientation: self.imageOrientation)

        return ret;
    

【讨论】:

@JsonCragun,输出图像模糊,图像中有小矩形。您在项目中使用上述代码吗?你面临这个问题吗?【参考方案3】:

请参阅 WWDC 上的 CoreImage 幻灯片。 你的回答很直接。

【讨论】:

以上是关于检测具有透明背景的 UIImage 的裁剪矩形的主要内容,如果未能解决你的问题,请参考以下文章

图像处理-裁剪具有透明背景的png

将裁剪蒙版应用于具有透明背景的动画 gif

如何检查 UIImage 是不是具有透明度

寻找具有透明背景的旋转图像的边界框

如何在 iOS 上删除 UIImage 中的矩形?

在具有透明背景的 Alert ViewController 中心添加图像