ios - 更改 UIImage 中的白色像素

标签 ios swift opencv uiimage

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

我的两张图片是这样的:

enter image description here

enter image description here

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

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

enter image description here

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

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

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

顺便说一句,使用 swift ......但感谢任何帮助,在此先感谢。

最佳答案

这可能对您有用——仅使用 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
    }

}

此扩展返回一个 UIImage,其中白色替换为传递的 UIColor,黑色“背景”变为透明。

以这种方式使用它:

// 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

这是一个完整的示例,使用您的两个原始图像(大部分代码设置 ImageView 以显示结果):

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 {

            $0.backgroundColor = .green

            NSLayoutConstraint.activate([

                $0.widthAnchor.constraint(equalToConstant: imageSize.width),
                $0.heightAnchor.constraint(equalToConstant: imageSize.height),

            ])

        }

    }

}

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

enter image description here

关于ios - 更改 UIImage 中的白色像素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58307979/

相关文章:

ios - 以不同方式突出显示 TextField 中的某些文本

ios - 将测试从一个单元测试目标导入到另一个

ios - 没有正确引用 Swift 对象中的随机数

python - 如何操纵跟踪器区域使其变为正方形?

opencv - 强度切片与 OpenCV 中的彩色 map 实现是否相同?

ios - 从 UIBarButtonItem 触发 textFieldShouldReturn

ios - NSPredicate 与 OR 返回错误

python-3.x - 使用 anaconda python 3.6 ( 在 mac 上) 安装 opencv 时遇到问题

ios - 在 Swift 中创建一个发出 HTTPRequest 的类

string - Swift - 从核心数据中获取字符串并以 UTF8 格式转换