簡體   English   中英

檢查 UIImage 的分段是亮還是暗

[英]Check if subsection of UIImage is light or dark

我試圖覆蓋一個 V 形按鈕,允許用戶關閉當前視圖。 人字形的顏色在深色圖像上應為淺色,在淺色圖像上應為深色。 我附上了我所描述的截圖。

在此處輸入圖像描述

但是,在嘗試計算圖像的明暗度時,會對性能產生重大影響,我正在這樣做(在 `CGImage 上操作):

var isDark: Bool {
    guard let imageData = dataProvider?.data else { return false }
    guard let ptr = CFDataGetBytePtr(imageData) else { return false }
    let length = CFDataGetLength(imageData)
    let threshold = Int(Double(width * height) * 0.45)
    var darkPixels = 0
    for i in stride(from: 0, to: length, by: 4) {
        let r = ptr[i]
        let g = ptr[i + 1]
        let b = ptr[i + 2]
        let luminance = (0.299 * Double(r) + 0.587 * Double(g) + 0.114 * Double(b))
        if luminance < 150 {
            darkPixels += 1
            if darkPixels > threshold {
                return true
            }
        }
    }
    return false
}

此外,人字形下方的特定區域較暗時效果不佳,但例如圖像的 rest 較亮。

我只想為圖像的一小部分計算它,因為 V 形非常小。 我嘗試使用 CGImage 的cropping(to rect: CGRect)裁剪圖像,但挑戰在於圖像設置為縱橫填充,這意味着 UIImageView 框架的頂部不是 UIImage 的頂部(例如,圖像可能被縮放在和居中)。 在通過方面填充調整圖像后,有沒有一種方法可以僅隔離出現在人字形框架下方的圖像部分?

編輯

由於接受的答案中的第一個鏈接,我能夠實現這一點。 我創建了一系列我認為應該適用於我以外的情況的擴展。

extension UIImage {
    var isDark: Bool {
        return cgImage?.isDark ?? false
    }
}

extension CGImage {
    var isDark: Bool {
        guard let imageData = dataProvider?.data else { return false }
        guard let ptr = CFDataGetBytePtr(imageData) else { return false }
        let length = CFDataGetLength(imageData)
        let threshold = Int(Double(width * height) * 0.45)
        var darkPixels = 0
        for i in stride(from: 0, to: length, by: 4) {
            let r = ptr[i]
            let g = ptr[i + 1]
            let b = ptr[i + 2]
            let luminance = (0.299 * Double(r) + 0.587 * Double(g) + 0.114 * Double(b))
            if luminance < 150 {
                darkPixels += 1
                if darkPixels > threshold {
                    return true
                }
            }
        }
        return false
    }

    func cropping(to rect: CGRect, scale: CGFloat) -> CGImage? {
        let scaledRect = CGRect(x: rect.minX * scale, y: rect.minY * scale, width: rect.width * scale, height: rect.height * scale)
        return self.cropping(to: scaledRect)
    }
}

extension UIImageView {
    func hasDarkImage(at subsection: CGRect) -> Bool {
        guard let image = image, let aspectSize = aspectFillSize() else { return false }
        let scale = image.size.width / frame.size.width
        let cropRect = CGRect(x: (aspectSize.width - frame.width) / 2,
                              y: (aspectSize.height - frame.height) / 2,
                              width: aspectSize.width,
                              height: frame.height)
        let croppedImage = image.cgImage?
            .cropping(to: cropRect, scale: scale)?
            .cropping(to: subsection, scale: scale)
        return croppedImage?.isDark ?? false
    }

    private func aspectFillSize() -> CGSize? {
        guard let image = image else { return nil }
        var aspectFillSize = CGSize(width: frame.width, height: frame.height)
        let widthScale = frame.width / image.size.width
        let heightScale = frame.height / image.size.height
        if heightScale > widthScale {
            aspectFillSize.width = heightScale * image.size.width
        }
        else if widthScale > heightScale {
            aspectFillSize.height = widthScale * image.size.height
        }
        return aspectFillSize
    }
}

一旦圖像適合視圖,這里有幾個選項可用於查找圖像的大小: How to know the image size after apply aspect fit for the image in an UIImageView

一旦你知道了,你就可以找出人字形的位置(你可能需要先轉換它的框架https://developer.apple.com/documentation/uikit/uiview/1622498-convert

如果性能仍然不足,我會考慮使用 CoreImage 來執行計算: https://www.hackingwithswift.com/example-code/media/how-to-read-the-average-color-of-a -uiimage-using-ciareaaverage

使用 CoreImage 有幾種方法,但獲取平均值是最簡單的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM