iOS 圖像處理(二):相機(jī)取色

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


前言

在上一篇文章 iOS 圖像處理(一):獲取某一點(diǎn)位置的像素 中有提到相機(jī)取色這么一個(gè)功能蛉鹿,其實(shí)這個(gè)功能在上篇文章的基礎(chǔ)上即可實(shí)現(xiàn)滨砍,現(xiàn)在來詳細(xì)講講;

實(shí)現(xiàn)

實(shí)現(xiàn)效果

因?yàn)橄鄼C(jī)功能需要用到真機(jī),要獲取效果圖比較麻煩惋戏,就不上了领追。

筆者思路
方式一

通過相機(jī)獲取得到圖像數(shù)據(jù),根據(jù)取色位置得到像素?cái)?shù)據(jù)响逢;

問題:因?yàn)檫@樣獲取得到的像素?cái)?shù)據(jù)是原數(shù)據(jù)绒窑,所以當(dāng)圖像進(jìn)行了一些變換操作(比如縮放),這時(shí)同樣要對位置進(jìn)行相應(yīng)的變換舔亭,實(shí)現(xiàn)起來相對麻煩些膨,所以不使用這種方式;

方式二

通過相機(jī)獲取得到圖像數(shù)據(jù)钦铺,將圖像數(shù)據(jù)渲染到 CALayer 上订雾,通過 CALayer 來獲取得到特定位置上的像素?cái)?shù)據(jù),這跟上一篇文章的方式二是完全一樣的职抡;

初始化圖像預(yù)覽層:

let previewLayer = CALayer()

internal func setupUI() {
    previewLayer.bounds = view.bounds
    previewLayer.position = view.center
    previewLayer.contentsGravity = kCAGravityResizeAspectFill
    previewLayer.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(M_PI / 2.0)))
    view.layer.insertSublayer(previewLayer, at: 0)
}

初始化相機(jī):

let session = AVCaptureSession()

// 相機(jī)數(shù)據(jù)幀接收隊(duì)列
let queue = DispatchQueue(label: "com.camera.video.queue")

// 取色位置
var center: CGPoint = .zero

internal func setupParameter() {
    
    session.beginConfiguration()
    session.sessionPreset = AVCaptureSessionPreset1280x720
    
    guard let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo),
        let deviceInput = try? AVCaptureDeviceInput(device: device) else {
        return
    }
    
    if session.canAddInput(deviceInput) {
        session.addInput(deviceInput)
    }
    
    let videoOutput = AVCaptureVideoDataOutput()
    videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable: NSNumber(value: kCMPixelFormat_32BGRA)]
    videoOutput.alwaysDiscardsLateVideoFrames = true
    videoOutput.setSampleBufferDelegate(self, queue: queue)
    
    if session.canAddOutput(videoOutput) {
        session.addOutput(videoOutput)
    }
    
    session.commitConfiguration()
}

設(shè)置取色位置:

public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {
        return
    }
    // 獲取用戶點(diǎn)擊位置在圖像上的相對位置
    let point = touch.location(in: self.view)
    center = point
    anchorButton.frame = CGRect(x: point.x - 20, y: point.y - 20 / 2, width: 40, height: 40)
}

獲取特定位置顏色:

public func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
    
    guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
        return
    }
    
    CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: CVOptionFlags(0)))
    
    guard let baseAddr = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0) else {
        return
    }
    let width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0)
    let height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0)
    let bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0)
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let bimapInfo: CGBitmapInfo = [
        .byteOrder32Little,
        CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)]
    
    guard let content = CGContext(data: baseAddr, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bimapInfo.rawValue) else {
        return
    }
    
    // 如果是從像素?cái)?shù)據(jù)中獲取特定位置像素葬燎,則是按以下被注釋的這部分代碼進(jìn)行獲取
    // 其中像素排序?yàn)椋築GRA,index 需要進(jìn)行要應(yīng)的變換
//        let data = baseAddr.assumingMemoryBound(to: UInt8.self)
//        let index = width * height * 2
//        let b = CGFloat(data.advanced(by: index + 0).pointee) / 255
//        let g = CGFloat(data.advanced(by: index + 1).pointee) / 255
//        let r = CGFloat(data.advanced(by: index + 2).pointee) / 255
//        let a = CGFloat(data.advanced(by: index + 3).pointee) / 255
//        let color = UIColor(red: r, green: g, blue: b, alpha: a)

    guard let cgImage = content.makeImage() else {
        return
    }
    
    DispatchQueue.main.async {
        self.previewLayer.contents = cgImage
        // self.previewLayer.pickColor 為 CALayer 的一個(gè)擴(kuò)展
        self.colorView.backgroundColor = self.previewLayer.pickColor(at: self.center)
    }
}
public extension CALayer {
    
    /// 獲取特定位置的顏色
    ///
    /// - parameter at: 位置
    ///
    /// - returns: 顏色
    public func pickColor(at position: CGPoint) -> UIColor? {
        
        // 用來存放目標(biāo)像素值
        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 原點(diǎn)偏移為目標(biāo)位置所有坐標(biāo)
        context.translateBy(x: -position.x, y: -position.y)
        // 將圖像渲染到 context 中
        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)
    }
}

本文到此結(jié)束谱净!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市擅威,隨后出現(xiàn)的幾起案子壕探,更是在濱河造成了極大的恐慌,老刑警劉巖郊丛,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件李请,死亡現(xiàn)場離奇詭異,居然都是意外死亡厉熟,警方通過查閱死者的電腦和手機(jī)导盅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揍瑟,“玉大人白翻,你說我怎么就攤上這事【钇” “怎么了滤馍?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長底循。 經(jīng)常有香客問我巢株,道長,這世上最難降的妖魔是什么熙涤? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任阁苞,我火速辦了婚禮困檩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘那槽。我一直安慰自己窗看,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布倦炒。 她就那樣靜靜地躺著,像睡著了一般软瞎。 火紅的嫁衣襯著肌膚如雪逢唤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天涤浇,我揣著相機(jī)與錄音鳖藕,去河邊找鬼。 笑死只锭,一個(gè)胖子當(dāng)著我的面吹牛著恩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜻展,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼喉誊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纵顾?” 一聲冷哼從身側(cè)響起伍茄,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎施逾,沒想到半個(gè)月后敷矫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汉额,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年曹仗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蠕搜。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怎茫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出讥脐,到底是詐尸還是另有隱情遭居,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布旬渠,位于F島的核電站俱萍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏告丢。R本人自食惡果不足惜枪蘑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一损谦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧岳颇,春花似錦照捡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瞻鹏,卻和暖如春悲立,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背新博。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工薪夕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赫悄。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓原献,卻偏偏與公主長得像,于是被迫代替她去往敵國和親埂淮。 傳聞我的和親對象是個(gè)殘疾皇子姑隅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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