首页 > 解决方案 > 使用 Swift 以编程方式从 Xcode 项目中的图像中删除白色

问题描述

我的 iOS 项目中有以下 JPG 图像:

在此处输入图像描述

我想使用 Swift 以编程方式从其中删除所有白色,以使背景透明并将其保存为 PNG。

我知道如何将图像保存为另一个文件夹中的 PNG,但我仍然不知道如何从图像中删除白色。

我可以使用任何库或代码

关于如何做到这一点的任何想法?

编辑:

根据@DuncanC 的建议,我从 Github 更新代码并尝试使用它。

我尝试使用以下代码操作图像

let a:UIImage = imagemSubViewPrincipal.image!
let rgbImage:RGBAImage = RGBAImage(image: a)!
for piece in rgbImage.pixels {
    print("\(piece.green) - \(piece.red)  - " )
}

编辑#2:

for piece in rgbImage.pixels {
  if piece.red >= 250 && piece.green >= 250 && piece.blue >= 250 {
    print("\(piece.red) - \(piece.green) - \(piece.blue) - \(piece.alpha)" )
    piece.alpha = 0
  }
}

我收到以下错误:

Cannot assign to property: 'piece' is a 'let' constant

如何创建 alpha 值更改为 0 的新图像。

编辑#3:

我能够使用下面的代码使其工作。

我能够获得透明图像。

for (index, piece) in rgbImage.pixels.enumerated()  {
        if piece.red >= 240 && piece.green >= 240 && piece.blue >= 240 {
            rgbImage.pixels[index] = transparentPixel
        }
    }

透明图片

图片的外部链接 https://snipboard.io/oGOb08.jpg

我能够将白色像素更改为另一种颜色,就像下面的示例一样

黄色背景的图像

我只是将透明像素更改为

let transparentPixel = Pixel(value: 0x00000000)
let yellowPixel = Pixel(value: 0xFF00FFFF)

@DuncanS 非常感谢您的帮助

标签: swiftxcodeimage

解决方案


一种方法是将图像转换为 RGBA 像素数据,查找所有白色像素,并将其 alpha 通道强制为 0。

一点谷歌搜索在 github 上揭示了这个要点。它定义了一个名为 RGBAImage 的结构,您使用 UIImage 对其进行初始化,并包含一个UnsafeMutableBufferPointerRBB 像素数据。

您应该能够将图像转换为 RGBAImage,然后遍历图像数据以查找白色像素并将其 alpha 强制为 0。您需要决定是否只想使用 R/G“剔除”纯白色像素/B 值为 255/255/255,或者如果您希望在匹配白色像素时出现一些“倾斜”。您可能希望将几乎白色的像素的 alpha 设置为几乎透明,这样您就可以将几乎白色的部分羽化为几乎透明的部分。

我首先将 255/255/255 像素的 alpha 设为 0,然后看看效果如何。如果你想要一些羽毛,你可以尝试添加它。

编辑:

我决定试一试,发现 Github 链接上的代码已经过时了。我已将其转换为 Swift 5 语法。这里是:

import UIKit

public struct Pixel {
    public var value: UInt32

    public var red: UInt8 {
        get {
            return UInt8(value & 0xFF)
        }
        set {
            value = UInt32(newValue) | (value & 0xFFFFFF00)
        }
    }

    public var green: UInt8 {
        get {
            return UInt8((value >> 8) & 0xFF)
        }
        set {
            value = (UInt32(newValue) << 8) | (value & 0xFFFF00FF)
        }
    }

    public var blue: UInt8 {
        get {
            return UInt8((value >> 16) & 0xFF)
        }
        set {
            value = (UInt32(newValue) << 16) | (value & 0xFF00FFFF)
        }
    }

    public var alpha: UInt8 {
        get {
            return UInt8((value >> 24) & 0xFF)
        }
        set {
            value = (UInt32(newValue) << 24) | (value & 0x00FFFFFF)
        }
    }
}

public struct RGBAImage {
    public var pixels: UnsafeMutableBufferPointer<Pixel>

    public var width: Int
    public var height: Int

    public init?(image: UIImage) {
        guard let cgImage = image.cgImage else { return nil }

        // Redraw image for correct pixel format
        let colorSpace = CGColorSpaceCreateDeviceRGB()

        var bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue
        bitmapInfo |= CGImageAlphaInfo.premultipliedLast.rawValue & CGBitmapInfo.alphaInfoMask.rawValue

        width = Int(image.size.width)
        height = Int(image.size.height)
        let bytesPerRow = width * 4

        let imageData = UnsafeMutablePointer<Pixel>.allocate(capacity:width * height)

        guard let imageContext = CGContext(data: imageData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else { return nil }
        imageContext.draw(cgImage, in: CGRect(origin: CGPoint.zero, size: image.size))

        pixels = UnsafeMutableBufferPointer<Pixel>(start: imageData, count: width * height)
    }

    public func toUIImage() -> UIImage? {
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        var bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue
        bitmapInfo |= CGImageAlphaInfo.premultipliedLast.rawValue & CGBitmapInfo.alphaInfoMask.rawValue

        let bytesPerRow = width * 4

        guard let imageContext = CGContext(data: pixels.baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo, releaseCallback: nil, releaseInfo: nil) else { return nil }

        guard let cgImage = imageContext.makeImage() else {return nil}
        let image = UIImage(cgImage: cgImage)

        return image
    }
}

编辑#2:

在玩您的图像时,我发现白色像素不是纯白色。如果您尝试匹配 255/255/255 的 R/G/B 值,它们将不匹配。我使用的代码在所有 3 个颜色通道中查找值 >250,并且有效。

我还发现我必须用 0x00000000 替换像素值。由于某种原因,将 Alpha 通道值设置为 0 不会强制像素透明。(我认为这与数据处于 premultipliedLast 模式这一事实有关,我并不完全理解。)

编辑#3:

这是我想出的演示应用程序的样子:

使用我发布的更新代码尝试一下。如果您遇到困难,请告诉我,我可以提供帮助。

在此处输入图像描述

编辑#4:

在更新问题的代码中,您尝试修改 for...in 循环获取的像素值。那是行不通的。您不能更改在这样的 for 循环中获取的值。使用这样的 for 循环:

let transparentPixel = Pixel(value: 0x00000000)
for (index, piece) in rgbImage.pixels.enumerated()  {
    // Your code to check and modify pixels
}

然后当您找到需要更改的像素时,使用类似的代码

rgbImage.pixels[index] = transparentPixel 

推荐阅读