使用透明背景检测UIImage的裁剪矩形

时间:2013-05-15 15:32:35

标签: iphone ios uiimage

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

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

我目前的应用是针对iOS 5.0,因此与之兼容是最佳选择。

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

3 个答案:

答案 0 :(得分:9)

您是否有机会看到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];
    }
}

答案 1 :(得分:0)

这里是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;
}

}

答案 2 :(得分:-5)

参考WWDC上的CoreImage幻灯片。 你的答案是直接的。