自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用函數(shù)式的 Swift 實(shí)現(xiàn)圖片轉(zhuǎn)字符畫(huà)的功能

移動(dòng)開(kāi)發(fā) iOS
今天整理 Pocket 中待看的文章,看到這篇《Creating ASCII art in functional Swift》,講解如何用 Swift 將圖片轉(zhuǎn)成 ASCII 字符。具體原理文中講解的很詳細(xì),不再贅述,但是標(biāo)題中的 in functional Swift 讓我很感興趣,想知道 functional 到底體現(xiàn)在哪里,于是下載 swift-ascii-art 源碼一探究竟。

今天整理 Pocket 中待看的文章,看到這篇《Creating ASCII art in functional Swift》,講解如何用 Swift 將圖片轉(zhuǎn)成 ASCII 字符。具體原理文中講解的很詳細(xì),不再贅述,但是標(biāo)題中的 in functional Swift 讓我很感興趣,想知道 functional 到底體現(xiàn)在哪里,于是下載 swift-ascii-art 源碼一探究竟。

Pixel

圖片是由各個(gè)像素點(diǎn)組成的,在代碼中像素通過(guò) Pixel 這個(gè) struct 實(shí)現(xiàn)。每個(gè)像素分配了4個(gè)字節(jié),這4個(gè)字節(jié) (2^8 = 256) 分別用來(lái)存儲(chǔ) RBGA 的值。

createPixelMatrix

可以通過(guò) createPixelMatrix 這個(gè)靜態(tài)方法創(chuàng)建一個(gè) width * height 像素矩陣:

  1. static func createPixelMatrix(width: Int, _ height: Int) -> [[Pixel]] { 
  2. return map(0.. map(0.. let offset = (width * row + col) * Pixel.bytesPerPixel 
  3. return Pixel(offset) 


和傳統(tǒng)方法中使用 for 循環(huán)來(lái)創(chuàng)建多維數(shù)組有所不同的是,這里是通過(guò) map 函數(shù)實(shí)現(xiàn)的。在 Swift 2.0 中, map 函數(shù)已經(jīng)被干掉了,只能作為方法調(diào)用。

intensityFromPixelPointer

intensityFromPixelPointer 方法計(jì)算并返回像素點(diǎn)的亮度值,代碼如下:

  1. func intensityFromPixelPointer(pointer: PixelPointer) -> Double { 
  2. let 
  3. red = pointer[offset + 0], 
  4. green = pointer[offset + 1], 
  5. blue = pointer[offset + 2
  6. return Pixel.calculateIntensity(red, green, blue) 
  7. private static func calculateIntensity(r: UInt8, _ g: UInt8, _ b: UInt8) -> Double { 
  8. let 
  9. redWeight = 0.229
  10. greenWeight = 0.587
  11. blueWeight = 0.114
  12. weightedMax = 255.0 * redWeight + 
  13. 255.0 * greenWeight + 
  14. 255.0 * blueWeight, 
  15. weightedSum = Double(r) * redWeight + 
  16. Double(g) * greenWeight + 
  17. Double(b) * blueWeight 
  18. return weightedSum / weightedMax 

calculateIntensity 方法基于 Y’UV 編碼獲取某個(gè)像素的亮度 (intensity) :

  1. Y’ = 0.299 R’ + 0.587 G’ + 0.114 B’ 

YUV 是一種顏色編碼方法,Y 表示亮度, UV 用來(lái)表示色差, U 和 V 是構(gòu)成彩色的兩個(gè)分量。它的優(yōu)點(diǎn)是可以利用人眼的特性來(lái)降低數(shù)字彩色圖像所需要的存儲(chǔ)容量。我們通過(guò)這個(gè)公式獲取到的 Y 就是亮度的值。

Offset

Pixel 中其實(shí)只存了一個(gè)值: offset 。 Pixel.createPixelMatrix 創(chuàng)建出來(lái)的矩陣是這樣的:

  1. [[048, ...], ...] 

并沒(méi)有像想象中那樣存儲(chǔ)了每個(gè)像素相關(guān)數(shù)據(jù),而更像是一個(gè)轉(zhuǎn)換工具,計(jì)算 PixelPointer 的灰度值。

AsciiArtist

AsciiArtist 里封裝了一些生成字符畫(huà)的方法。

createAsciiArt

createAsciiArt 方法就是創(chuàng)建字符畫(huà):

  1. func createAsciiArt() -> String { 
  2. let 
  3. // 加載圖片數(shù)據(jù),獲取指針對(duì)象 
  4. dataProvider = CGImageGetDataProvider(image.CGImage), 
  5. pixelData = CGDataProviderCopyData(dataProvider), 
  6. pixelPointer = CFDataGetBytePtr(pixelData), 
  7. // 將圖片轉(zhuǎn)成亮度值矩陣 
  8. intensities = intensityMatrixFromPixelPointer(pixelPointer), 
  9. // 將亮度值轉(zhuǎn)成對(duì)應(yīng)字符 
  10. symbolMatrix = symbolMatrixFromIntensityMatrix(intensities) 
  11. return join("\n", symbolMatrix) 

其中 CFDataGetBytePtr 函數(shù)返回了圖像的字節(jié)數(shù)組指針,數(shù)組里每個(gè)元素都是一個(gè)字節(jié),即 0~255 的整數(shù)。每4個(gè)字節(jié)組成了一個(gè) Pixel ,分別對(duì)應(yīng)著 RGBA 的值。

intensityMatrixFromPixelPointer

intensityMatrixFromPixelPointer 這個(gè)方法是通過(guò) PixelPointer 生成對(duì)應(yīng)的亮度值矩陣:

  1. private func intensityMatrixFromPixelPointer(pointer: PixelPointer) -> [[Double]] 
  2. let 
  3. width = Int(image.size.width), 
  4. height = Int(image.size.height), 
  5. matrix = Pixel.createPixelMatrix(width, height) 
  6. return matrix.map { pixelRow in 
  7. pixelRow.map { pixel in 
  8. pixel.intensityFromPixelPointer(pointer) 

首先通過(guò) Pixel.createPixelMatrix 方法創(chuàng)建了一個(gè)空的二維數(shù)組,用來(lái)存放數(shù)值。然后用兩個(gè) map 嵌套遍歷里面的所有元素,將像素 (pixel) 轉(zhuǎn)換成亮度 (intensity) 的值。

symbolMatrixFromIntensityMatrix

symbolMatrixFromIntensityMatrix 函數(shù)將亮度值數(shù)組轉(zhuǎn)換成字符畫(huà)數(shù)組:

  1. private func symbolMatrixFromIntensityMatrix(matrix: [[Double]]) -> [String] 
  2. return matrix.map { intensityRow in 
  3. intensityRow.reduce("") { 
  4. $0 + self.symbolFromIntensity($1

map + reduce 成功實(shí)現(xiàn)了字符串的累加,每次 reduce 都是通過(guò) symbolFromIntensity 方法獲取到亮度值對(duì)應(yīng)的字符。 symbolFromIntensity 方法如下:

  1. private func symbolFromIntensity(intensity: Double) -> String 
  2. assert(0.0 <= intensity && intensity <= 1.0
  3. let 
  4. factor = palette.symbols.count - 1
  5. value = round(intensity * Double(factor)), 
  6. index = Int(value) 
  7. return palette.symbols[index] 

傳入 intensity ,在確保了值的范圍是 0 ~ 1 之后,通過(guò) AsciiPalette 將它轉(zhuǎn)換成對(duì)應(yīng)的字符,輸出 sumbol 。

AsciiPalette

AsciiPalette 是用來(lái)將數(shù)值轉(zhuǎn)換成字符的工具,像是一個(gè)字符畫(huà)里的調(diào)色板一樣,根據(jù)不同的顏色生成字符。

loadSymbols

loadSymbols 加載了所有的字符:

  1. private func loadSymbols() -> [String] 
  2. return symbolsSortedByIntensityForAsciiCodes(32...126// from ' ' to '~' 

可以看到,我們選用的字符范圍是 32 ~ 126 的字符,接下來(lái)就是通過(guò) symbolsSortedByIntensityForAsciiCodes 方法將這些字符按照亮度進(jìn)行排序。比如 & 符號(hào)肯定代表著比 . 暗的區(qū)域,那么它是如何比較的呢?請(qǐng)看排序方法。

symbolsSortedByIntensityForAsciiCodes

symbolsSortedByIntensityForAsciiCodes 方法實(shí)現(xiàn)了字符串的生成和排序:

  1. private func symbolsSortedByIntensityForAsciiCodes(codes: Range) -> [String] 
  2. let 
  3. // 通過(guò) Ascii 碼生成字符數(shù)組備用 
  4. symbols = codes.map { self.symbolFromAsciiCode($0) }, 
  5. // 將字符繪制出來(lái),把字符數(shù)組轉(zhuǎn)換成圖片數(shù)組,用于比較亮度 
  6. symbolImages = symbols.map { UIImage.imageOfSymbol($0, self.font) }, 
  7. // 將圖片數(shù)組轉(zhuǎn)換成亮度值數(shù)組,亮度值的表現(xiàn)形式是圖片中白色像素的個(gè)數(shù) 
  8. whitePixelCounts = symbolImages.map { self.countWhitePixelsInImage($0) }, 
  9. // 將字符數(shù)組通過(guò)亮度值就行排序 
  10. sortedSymbols = sortByIntensity(symbols, whitePixelCounts) 
  11. return sortedSymbols 

其中, sortByIntensity 這個(gè)排序方法如下:

  1. private func sortByIntensity(symbols: [String], _ whitePixelCounts: [Int]) -> [String] 
  2. let 
  3. // 用字典建立 白色像素?cái)?shù)目 和 字符 之間的關(guān)系 
  4. mappings = NSDictionary(objects: symbols, forKeys: whitePixelCounts), 
  5. // 白色像素?cái)?shù)目數(shù)組去重 
  6. uniqueCounts = Set(whitePixelCounts), 
  7. // 白色像素?cái)?shù)目數(shù)組排序 
  8. sortedCounts = sorted(uniqueCounts), 
  9. // 利用前面的字典映射,將排序后的白色像素?cái)?shù)目轉(zhuǎn)換成對(duì)應(yīng)的字符,從而輸出有序數(shù)組 
  10. sortedSymbols = sortedCounts.map { mappings[$0] as! String } 
  11. return sortedSymbols 

小結(jié)

簡(jiǎn)單了過(guò)了一下項(xiàng)目,可以隱約感覺(jué)到一些函數(shù)式風(fēng)格的氣息,主要體現(xiàn)在一下幾個(gè)方面:

map reduce 等函數(shù)的應(yīng)用恰到好處,自如處理數(shù)組的轉(zhuǎn)換和拼接。

通過(guò) input 和 output 進(jìn)行數(shù)據(jù)處理,比如 sortByIntensity 方法和 symbolFromIntensity 方法。

很少有狀態(tài)和屬性,更多的是直接的函數(shù)轉(zhuǎn)換,函數(shù)邏輯不依賴外部變量,只依賴于傳入的參數(shù)

代碼感覺(jué)簡(jiǎn)單輕快。通過(guò)這個(gè)簡(jiǎn)單的小例子,驗(yàn)證了前面在 函數(shù)式的特性 中學(xué)習(xí)到的東西。

感覺(jué)很贊!

責(zé)任編輯:chenqingxiang 來(lái)源: Why's blog
相關(guān)推薦

2015-08-03 11:42:27

Swift漢堡式過(guò)度動(dòng)畫(huà)

2015-03-18 09:39:10

函數(shù)編程Swift

2009-11-26 13:52:07

PHP字符串替換函數(shù)s

2013-07-23 13:06:50

2023-09-08 09:12:57

內(nèi)存緩存圖像

2021-08-26 05:27:57

Swift 監(jiān)聽(tīng)系統(tǒng)泛型

2014-07-15 09:26:13

swiftiosMPGNotifica

2022-12-30 17:18:04

2009-06-16 11:49:00

JQuery實(shí)現(xiàn)loaWCF

2009-11-30 15:10:46

PHP substr函

2009-12-07 16:44:45

PHP圖形處理函數(shù)

2009-09-11 10:44:07

JavaScript實(shí)

2009-11-30 17:49:51

PHP函數(shù)preg_s

2021-08-27 08:38:10

CSS 技巧 resize

2017-09-01 15:42:00

MySQLOracledblink功能

2009-06-19 14:23:41

RMIJava分布式計(jì)算

2021-12-09 10:45:19

分布式事務(wù)框架

2021-02-25 15:14:12

鴻蒙HarmonyOS應(yīng)用開(kāi)發(fā)

2012-05-17 16:34:28

思亞諾美滿電子

2015-01-06 16:41:32

Swiftenum多選
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)