iOS 圖像處理(一):獲取某一點位置的像素

本文是筆者用來記錄學(xué)習(xí) iOS 圖像處理相關(guān)的文章截亦,內(nèi)容可能有部分不對额划,可以通過評論或私信交流工闺。本文所有代碼都是 Swift 3.0


前言

一直以來度苔,筆者很想實現(xiàn)這么一個功能:獲取一張圖片,用戶可以從圖片中進行取色暂吉,更進一步胖秒,可以通過相機對現(xiàn)實中的景色進行取色;

現(xiàn)在就以從圖片中取色這么一個功能來作為開篇慕的。

實現(xiàn)

實現(xiàn)效果
獲取顏色.gif
筆者思路

最開始阎肝,對于這么一個功能,筆者的思路是:獲取圖片所有像素肮街,將用戶點擊的位置轉(zhuǎn)換為圖片中的像素索引风题,獲取目標像素。

獲取圖片所有像素:

public extension UIImage {
    
    /// 根據(jù)圖片大小提取像素
    ///
    /// - parameter size: 圖片大小
    ///
    /// - returns: 像素數(shù)組
    public func extraPixels(in size: CGSize) -> [UInt32]? {
        
        guard let cgImage = cgImage else {
            return nil
        }
        
        let width = Int(size.width)
        let height = Int(size.height)
        // 一個像素 4 個字節(jié)嫉父,則一行共 4 * width 個字節(jié)
        let bytesPerRow = 4 * width
        // 每個像素元素位數(shù)為 8 bit沛硅,即 rgba 每位各 1 個字節(jié)
        let bitsPerComponent = 8
        // 顏色空間為 RGB,這決定了輸出顏色的編碼是 RGB 還是其他(比如 YUV)
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        // 設(shè)置位圖顏色分布為 RGBA
        let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue
        
        var pixelsData = [UInt32](repeatElement(0, count: width * height))
        
        guard let content = CGContext(data: &pixelsData, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
            return nil
        }
        
        content.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        return pixelsData
    }
}

根據(jù)用戶點擊位置獲取顏色:

public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
    // 獲取用戶點擊位置在圖像上的相對位置
    let point = touch.location(in: imageView0)
    // 將點擊位置轉(zhuǎn)換為像素索引(實際應(yīng)用時此處應(yīng)做空判斷)
    let pixelIndex = imageView0.pixelIndex(for: point) ?? 0
    // 根據(jù)圖像大小提取圖像像素(實際應(yīng)用時此處應(yīng)做空判斷)
    let pixels = imageView0.pixels ?? []
    // 獲取目標像素(實際應(yīng)用時此處應(yīng)做范圍判斷)
    view.backgroundColor = imageView0.extraColor(for: pixels[pixelIndex])
}
public extension UIImageView {
    
    /// 圖像像素
    public var pixels: [UInt32]? {
        return image?.extraPixels(in: bounds.size)
    }
    
    /// 將位置轉(zhuǎn)換為像素索引
    ///
    /// - parameter point: 位置
    ///
    /// - returns: 像素索引
    public func pixelIndex(for point: CGPoint) -> Int? {
        let size = bounds.size
        guard point.x > 0 && point.x <= size.width
            && point.y > 0 && point.y <= size.height else {
            return nil
        }
        return (Int(point.y) * Int(size.width) + Int(point.x))
    }
    
    /// 將像素值轉(zhuǎn)換為顏色
    ///
    /// - parameter pixel: 像素值
    ///
    /// - returns: 顏色
    public func extraColor(for pixel: UInt32) -> UIColor {
        let r = Int((pixel >> 0) & 0xff)
        let g = Int((pixel >> 8) & 0xff)
        let b = Int((pixel >> 16) & 0xff)
        let a = Int((pixel >> 24) & 0xff)
        return UIColor(intRed: r, green: g, blue: b, alpha: a)
    }
}

需要注意:

  1. 上面 touchesBegan 中是每次點擊都去根據(jù)當前 UIImageView 的大小獲取圖像所有像素绕辖,實際應(yīng)用中可在初始化時獲取并保存摇肌,點擊的時候直接調(diào)用即可;

  2. 提取圖像像素時需要根據(jù)當前顯示的大小進行提取仪际,這是因為使用 UIImageView 顯示圖像時围小,圖像會根據(jù)顯示大小進行縮放偷拔,此時顯示的像素分布與 UIImage 的像素分布是完全不同的桑包,因此在獲取某一點的像素锨络,必須根據(jù)當前顯示大小進行處理升熊;

另一種實現(xiàn)

上面的實現(xiàn)需要先獲取圖像的所有像素數(shù)據(jù),比較占內(nèi)存晤郑;有另一種實現(xiàn)书斜,不需要獲取所有像素數(shù)據(jù):

public extension UIView {
    
    /// 獲取特定位置的顏色
    ///
    /// - parameter at: 位置
    ///
    /// - returns: 顏色
    public func pickColor(at position: CGPoint) -> UIColor? {
        
        // 用來存放目標像素值
        var pixel = [UInt8](repeatElement(0, count: 4))
        // 顏色空間為 RGB牡借,這決定了輸出顏色的編碼是 RGB 還是其他(比如 YUV)
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        // 設(shè)置位圖顏色分布為 RGBA
        let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue
        guard let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo) else {
            return nil
        }
        // 設(shè)置 context 原點偏移為目標位置所有坐標
        context.translateBy(x: -position.x, y: -position.y)
        // 將圖像渲染到 context 中
        layer.render(in: context)
        
        return UIColor(red: CGFloat(pixel[0]) / 255.0,
                       green: CGFloat(pixel[1]) / 255.0,
                       blue: CGFloat(pixel[2]) / 255.0,
                       alpha: CGFloat(pixel[3]) / 255.0)
    }
}

根據(jù)用戶點擊位置獲取顏色:

public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
    // 獲取用戶點擊位置在圖像上的相對位置
    let point = touch.location(in: imageView0)
    view.backgroundColor = imageView0.pickColor(at: point)
}

可以看到筆者將 pickColor 作為 UIView 的擴展伦连,因為這適用于所有擁有 layer 層的對象雨饺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惑淳,隨后出現(xiàn)的幾起案子额港,更是在濱河造成了極大的恐慌,老刑警劉巖歧焦,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件移斩,死亡現(xiàn)場離奇詭異,居然都是意外死亡绢馍,警方通過查閱死者的電腦和手機向瓷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舰涌,“玉大人猖任,你說我怎么就攤上這事〈砂遥” “怎么了朱躺?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長搁痛。 經(jīng)常有香客問我长搀,道長,這世上最難降的妖魔是什么鸡典? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任源请,我火速辦了婚禮,結(jié)果婚禮上彻况,老公的妹妹穿的比我還像新娘谁尸。我一直安慰自己,他們只是感情好疗垛,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布症汹。 她就那樣靜靜地躺著,像睡著了一般贷腕。 火紅的嫁衣襯著肌膚如雪背镇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天泽裳,我揣著相機與錄音瞒斩,去河邊找鬼。 笑死涮总,一個胖子當著我的面吹牛胸囱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瀑梗,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼烹笔,長吁一口氣:“原來是場噩夢啊……” “哼裳扯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谤职,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤饰豺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后允蜈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冤吨,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年饶套,在試婚紗的時候發(fā)現(xiàn)自己被綠了漩蟆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡妓蛮,死狀恐怖怠李,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仔引,我是刑警寧澤扔仓,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站咖耘,受9級特大地震影響翘簇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜儿倒,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一版保、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夫否,春花似錦彻犁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至微谓,卻和暖如春森篷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背豺型。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工仲智, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人姻氨。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓钓辆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子前联,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容