【问题标题】:Change White Pixels in a UIImage更改 UIImage 中的白色像素
【发布时间】:2020-02-07 00:25:01
【问题描述】:

我有一个 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 只返回红色轮廓而不是白色轮廓,那就太好了,但我认为不可能改变这一点(也许我错了)。

使用 swift btw...但任何帮助表示赞赏,在此先感谢。

【问题讨论】:

  • 你试过this吗?
  • 您希望“填充”为灰色吗?还是留白?或者可以选择其他颜色?
  • 只要它的三种不同颜色,任何颜色都是毫无疑问的,我基本上希望能够找到背景、边缘或前景作为三种距离颜色
  • @AngryDuck - 我使用纯 Swift 代码添加了一个可能的解决方案。

标签: ios swift opencv uiimage


【解决方案1】:

试试这个:

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!
}

【讨论】:

    【解决方案2】:

    这个想法是将bitwise-or 两个掩码“合并”在一起,这两个掩码。由于这个新的组合灰度图像仍然是单个(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 功能,速度似乎相当快

    【讨论】:

    • combine[outline > 120] = (57,0,204) 在做什么有解释吗?烦人的是,ObjectiveC(项目很快,但openCV的东西在iOS的objective c中)功能采用不同的参数,似乎没有相同的界面,任何知道的人都可以转换它吗?
    • 蒙版是灰度图像(1通道),像素强度在[0...255]范围内,所以combine[outline > 120] = (57,0,204)本质上是说,轮廓蒙版上任何大于120的像素值,更改它在合并的图像上变为红色。由于某种原因,输入图像未固定为 10 强度,否则 combine[outline > 0] 会起作用,因为它说蒙版上的任何白色像素都会在合并图像上将其更改为红色
    【解决方案3】:

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

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

    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),
    
                ])
    
            }
    
        }
    
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-04
      • 1970-01-01
      相关资源
      最近更新 更多