更改 UIImage 中的白色像素

Change White Pixels in a UIImage

我有一个 UIImage,它是一个轮廓,一个是填充的,两者都是从 OpenCV 创建的,一个来自抓取,另一个来自结构化元素。

我的两张图是这样的:

我试图更改轮廓图像中的所有白色像素,因为我想将两个图像合并在一起,所以我最终得到一个红色轮廓和中间的白色填充区域。我正在使用它来将两者合并在一起,我知道它不是红色而是粉红色,灰色而不是白色,因为我只是将它们与 alpha 混合在一起。

// Start an image context
UIGraphicsBeginImageContext(grabcutResult.size)
        
// Create rect for this draw session
let rect = CGRect(x: 0.0, y: 0.0, width: grabcutResult.size.width, height: grabcutResult.size.height)
grabcutResult.draw(in: rect)
redGrabcutOutline.draw(in: rect, blendMode: .normal, alpha: 0.5)

let finalImage = UIGraphicsGetImageFromCurrentImageContext()

想法是它应该看起来像这样。

我希望能够快速完成此操作,但我找到的唯一解决方案是 ImageView(它只影响内容的呈现方式而不是底层 UIImage),或者它们涉及逐像素循环遍历整个图像.

我正在尝试找到一种解决方案,将轮廓中的所有白色像素都屏蔽为红色,而不必逐个像素地遍历整个图像,因为它太慢了。

理想情况下,如果我能让 openCV 变成 return 红色轮廓而不是白色轮廓,那会很好,但我认为它不可能改变它(也许我错了)。

使用 swift 顺便说一句...但感谢任何帮助,提前致谢。

试试这个:

public func maskWithColor2( color:UIColor) -> UIImage {

    UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale)
    let context = UIGraphicsGetCurrentContext()!

    color.setFill()

    context.translateBy(x: 0, y: self.size.height)
    context.scaleBy(x: 1.0, y: -1.0)

    let rect = CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height)
    context.draw(self.cgImage!, in: rect)

    context.setBlendMode(CGBlendMode.sourceIn)
    context.addRect(rect)
    context.drawPath(using: CGPathDrawingMode.fill)

    let coloredImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return coloredImage!
}

思路是把bitwise-or两个mask连在一起"merges"两个mask。由于这个新的组合灰度图像仍然是单个(1 通道)图像,我们需要将其转换为 3 通道,以便我们可以对图像应用颜色。最后,我们将轮廓蒙版涂成红色以获得结果

我在 Python OpenCV 中实现了它,但您可以在 Swift

中采用相同的想法
import cv2

# Read in images as grayscale
full = cv2.imread('1.png', 0)
outline = cv2.imread('2.png', 0)

# Bitwise-or masks
combine = cv2.bitwise_or(full, outline)

# Combine to 3 color channel and color outline red
combine = cv2.merge([combine, combine, combine])
combine[outline > 120] = (57,0,204)

cv2.imshow('combine', combine)
cv2.waitKey()

使用 IPython

的基准
In [3]: %timeit combine()
782 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

利用了Numpy的Vectorized特性,好像还挺快的

这可能对您有用 - 仅使用 Swift 代码...

extension UIImage {

    func maskWithColor(color: UIColor) -> UIImage? {

        let maskingColors: [CGFloat] = [1, 255, 1, 255, 1, 255]
        let bounds = CGRect(origin: .zero, size: size)

        let maskImage = cgImage!
        var returnImage: UIImage?

        // make sure image has no alpha channel
        let rFormat = UIGraphicsImageRendererFormat()
        rFormat.opaque = true
        let renderer = UIGraphicsImageRenderer(size: size, format: rFormat)
        let noAlphaImage = renderer.image {
            (context) in
            self.draw(at: .zero)
        }

        let noAlphaCGRef = noAlphaImage.cgImage

        if let imgRefCopy = noAlphaCGRef?.copy(maskingColorComponents: maskingColors) {

            let rFormat = UIGraphicsImageRendererFormat()
            rFormat.opaque = false
            let renderer = UIGraphicsImageRenderer(size: size, format: rFormat)
            returnImage = renderer.image {
                (context) in
                context.cgContext.clip(to: bounds, mask: maskImage)
                context.cgContext.setFillColor(color.cgColor)
                context.cgContext.fill(bounds)
                context.cgContext.draw(imgRefCopy, in: bounds)
            }

        }
        return returnImage
    }

}

此扩展returns一个UIImage白色替换为通过的UIColor,黑色"background"变为透明

这样使用:

// change filled white star to gray with transparent background
let modFilledImage = filledImage.maskWithColor(color: UIColor(red: 200, green: 200, blue: 200))

// change outlined white star to red with transparent background
let modOutlineImage = outlineImage.maskWithColor(color: UIColor.red)

// combine the images on a black background

这里是一个完整的例子,使用你的两个原始图像(大部分代码是设置图像视图来显示结果):

extension UIImage {

    func maskWithColor(color: UIColor) -> UIImage? {

        let maskingColors: [CGFloat] = [1, 255, 1, 255, 1, 255]
        let bounds = CGRect(origin: .zero, size: size)

        let maskImage = cgImage!
        var returnImage: UIImage?

        // make sure image has no alpha channel
        let rFormat = UIGraphicsImageRendererFormat()
        rFormat.opaque = true
        let renderer = UIGraphicsImageRenderer(size: size, format: rFormat)
        let noAlphaImage = renderer.image {
            (context) in
            self.draw(at: .zero)
        }

        let noAlphaCGRef = noAlphaImage.cgImage

        if let imgRefCopy = noAlphaCGRef?.copy(maskingColorComponents: maskingColors) {

            let rFormat = UIGraphicsImageRendererFormat()
            rFormat.opaque = false
            let renderer = UIGraphicsImageRenderer(size: size, format: rFormat)
            returnImage = renderer.image {
                (context) in
                context.cgContext.clip(to: bounds, mask: maskImage)
                context.cgContext.setFillColor(color.cgColor)
                context.cgContext.fill(bounds)
                context.cgContext.draw(imgRefCopy, in: bounds)
            }

        }
        return returnImage
    }

}

class MaskWorkViewController: UIViewController {

    let origFilledImgView: UIImageView = {
        let v = UIImageView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.contentMode = .center
        return v
    }()

    let origOutlineImgView: UIImageView = {
        let v = UIImageView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.contentMode = .center
        return v
    }()

    let modifiedFilledImgView: UIImageView = {
        let v = UIImageView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.contentMode = .center
        return v
    }()

    let modifiedOutlineImgView: UIImageView = {
        let v = UIImageView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.contentMode = .center
        return v
    }()

    let combinedImgView: UIImageView = {
        let v = UIImageView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.contentMode = .center
        return v
    }()

    let origStack: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .horizontal
        v.spacing = 20
        return v
    }()

    let modifiedStack: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .horizontal
        v.spacing = 20
        return v
    }()

    let mainStack: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.alignment = .center
        v.spacing = 10
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let filledImage = UIImage(named: "StarFill"),
            let outlineImage = UIImage(named: "StarEdge") else {
                return
        }

        var modifiedFilledImage: UIImage = UIImage()
        var modifiedOutlineImage: UIImage = UIImage()
        var combinedImage: UIImage = UIImage()

        // for both original images, replace white with color
        // and make black transparent
        if let modFilledImage = filledImage.maskWithColor(color: UIColor(red: 200, green: 200, blue: 200)),
            let modOutlineImage = outlineImage.maskWithColor(color: UIColor.red) {

            modifiedFilledImage = modFilledImage
            modifiedOutlineImage = modOutlineImage

            let rFormat = UIGraphicsImageRendererFormat()
            rFormat.opaque = true

            let renderer = UIGraphicsImageRenderer(size: modifiedFilledImage.size, format: rFormat)

            // combine modified images on black background
            combinedImage = renderer.image {
                (context) in
                context.cgContext.setFillColor(UIColor.black.cgColor)
                context.cgContext.fill(CGRect(origin: .zero, size: modifiedFilledImage.size))
                modifiedFilledImage.draw(at: .zero)
                modifiedOutlineImage.draw(at: .zero)
            }

        }

        // setup image views and set .image properties
        setupUI(filledImage.size)

        origFilledImgView.image = filledImage
        origOutlineImgView.image = outlineImage

        modifiedFilledImgView.image = modifiedFilledImage
        modifiedOutlineImgView.image = modifiedOutlineImage

        combinedImgView.image = combinedImage

    }

    func setupUI(_ imageSize: CGSize) -> Void {

        origStack.addArrangedSubview(origFilledImgView)
        origStack.addArrangedSubview(origOutlineImgView)

        modifiedStack.addArrangedSubview(modifiedFilledImgView)
        modifiedStack.addArrangedSubview(modifiedOutlineImgView)

        var lbl = UILabel()
        lbl.textAlignment = .center
        lbl.text = "Original Images"

        mainStack.addArrangedSubview(lbl)

        mainStack.addArrangedSubview(origStack)

        lbl = UILabel()
        lbl.textAlignment = .center
        lbl.numberOfLines = 0
        lbl.text = "Modified Images\n(UIImageViews have Green Background)"

        mainStack.addArrangedSubview(lbl)

        mainStack.addArrangedSubview(modifiedStack)

        lbl = UILabel()
        lbl.textAlignment = .center
        lbl.text = "Combined on Black Background"

        mainStack.addArrangedSubview(lbl)

        mainStack.addArrangedSubview(combinedImgView)

        view.addSubview(mainStack)

        NSLayoutConstraint.activate([

            mainStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
            mainStack.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0),

        ])

        [origFilledImgView, origOutlineImgView, modifiedFilledImgView, modifiedOutlineImgView, combinedImgView].forEach {

            [=12=].backgroundColor = .green

            NSLayoutConstraint.activate([

                [=12=].widthAnchor.constraint(equalToConstant: imageSize.width),
                [=12=].heightAnchor.constraint(equalToConstant: imageSize.height),

            ])

        }

    }

}

以及结果,显示原始、修改和最终组合图像...图像视图具有绿色背景以显示透明区域: