swift - 使用 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 非常感谢您的帮助
解决方案
一种方法是将图像转换为 RGBA 像素数据,查找所有白色像素,并将其 alpha 通道强制为 0。
一点谷歌搜索在 github 上揭示了这个要点。它定义了一个名为 RGBAImage 的结构,您使用 UIImage 对其进行初始化,并包含一个UnsafeMutableBufferPointer
RBB 像素数据。
您应该能够将图像转换为 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
推荐阅读
- mysql - 我需要为表中的字段设置什么数据类型,以限制一组选项中的值?
- karate - afterScenario 钩子上的 karate.log(args) 未嵌入到 surefire json 文件中
- javascript - 如何使用带有 Socket.IO 的轮询器向每个人发送
- wordpress - 如何在不破坏 WordPress 的情况下使用 .htaccess 向 URL 添加参数
- reactjs - 如何为松弛克隆创建新通道按钮
- go - 如何从 Go 模板调用外部程序
- sql - 插入时的 SSIS 错误 - 维度表中的新主键时出错
- java - 一种递归方法和一种迭代方法的空堆栈
- android - 拦截 NavigationUI.onNavDestinationSelected() 以使用“inclusive = true”使 backstack 弹出
- c - 蓝牙通信延迟大