|
1 | 1 | //#-hidden-code
|
2 | 2 | //
|
3 |
| -// See LICENSE folder for this template’s licensing information. |
4 |
| -// |
5 |
| -// Abstract: |
6 |
| -// The Swift file containing the source code edited by the user of this playground book. |
| 3 | +// Copyright © 2020 Bunny Wong |
| 4 | +// Created on 2019/12/18. |
7 | 5 | //
|
| 6 | + |
| 7 | +import UIKit |
| 8 | +import PlaygroundSupport |
| 9 | +import Accelerate |
| 10 | + |
| 11 | +import BookCore |
| 12 | + |
| 13 | +PlaygroundPage.current.needsIndefiniteExecution = true |
| 14 | + |
| 15 | +//#-end-hidden-code |
| 16 | +/*: |
| 17 | +# Grayscale, Histogram & Equalization |
| 18 | + |
| 19 | +## Grayscale |
| 20 | + |
| 21 | +To achieve the final result, the first thing we need to do is converting the image into a *grayscaled* version. |
| 22 | +We can achieve this by setting it's red, green and blue scalar to the same value, which is the average. With prior |
| 23 | +knowledge,this task should be simple now. Just construct a grayscale matrix and… it's done. |
| 24 | + |
| 25 | +However, according to scientific researches, our eyes are more sensitive to green color than red and blue (actually, |
| 26 | +green takes up almost 70%). To achieve better result, we can modify our coefficients a little bit, taking this into |
| 27 | +consideration. |
| 28 | + |
| 29 | +### 🔨Yet Another Image Filter |
| 30 | + |
| 31 | +* Experiment: |
| 32 | + * In this experiment, we'll build another filter to turn a image into grayscaled version, with consideration. |
| 33 | + * Try to read and complete the following code snippet. When you finish, run your code and tap the *Switch to |
| 34 | + Grayscale* button below the image to see whether it works. |
| 35 | +*/ |
| 36 | + |
| 37 | +//#-code-completion(everything, hide) |
| 38 | +//#-code-completion(literal, show, float, integer) |
| 39 | +//#-code-completion(identifier, show, coefficientRed, coefficientGreen, coefficientBlue) |
| 40 | +//#-editable-code |
| 41 | +func applyGrayscaleFilter(rawImage: RawImage) { |
| 42 | + let coefficientRed: Float = 0.2126 |
| 43 | + let coefficientGreen: Float = 0.7152 |
| 44 | + let coefficientBlue: Float = 0.0722 |
| 45 | + var filterMatrix: [Float] = [ |
| 46 | + <#T##Red##Float#>, <#T##Red##Float#>, <#T##Red##Float#>, 0, |
| 47 | + <#T##Green##Float#>, <#T##Green##Float#>, <#T##Green##Float#>, 0, |
| 48 | + <#T##Blue##Float#>, <#T##Blue##Float#>, <#T##Blue##Float#>, 0, |
| 49 | + 0, 0, 0, 1 |
| 50 | + ] |
| 51 | + rawImage.multiplyByMatrix(matrix4x4: filterMatrix) |
| 52 | +} |
| 53 | + |
| 54 | +//#-end-editable-code |
| 55 | +/*: |
| 56 | +## Histograms |
| 57 | + |
| 58 | +Now we've turned our image into grayscale and here comes another problem we have to solve: for some images, which are |
| 59 | +too bright or too dark, the resulting ASCII arts may be hard to recognize, as the following figure shows. |
| 60 | + |
| 61 | +**Histogram** is an effective tool for image processing. It visualizes the distribution of tones in an image. The X axis |
| 62 | +represents the brightness, while the Y axis represents the relative number of pixels at that brightness value. The |
| 63 | +following figure shows the same images as previous figure, along with histograms representing them. |
| 64 | + |
| 65 | +### 🔬Demystification of Histograms |
| 66 | + |
| 67 | +* Experiment: |
| 68 | + * Choose an image and then tap the *Show Histogram* icon below the image to show the histogram. Tap it again to |
| 69 | + see a histogram with separated red, green, and blue value. |
| 70 | + * Try to understand these graphs by associating them with tone and color distributions of images. |
| 71 | +*/ |
| 72 | +//#-editable-code |
| 73 | +func applyGrayscaleFilterx(rawImage: RawImage) { |
| 74 | + let coefficientRed: Float = 0.2126 |
| 75 | + let coefficientGreen: Float = 0.7152 |
| 76 | + let coefficientBlue: Float = 0.0722 |
| 77 | + var filterMatrix: [Float] = [ |
| 78 | + <#T##Red##Float#>, <#T##Red##Float#>, <#T##Red##Float#>, 0, |
| 79 | + <#T##Green##Float#>, <#T##Green##Float#>, <#T##Green##Float#>, 0, |
| 80 | + <#T##Blue##Float#>, <#T##Blue##Float#>, <#T##Blue##Float#>, 0, |
| 81 | + 0, 0, 0, 1 |
| 82 | + ] |
| 83 | + rawImage.multiplyByMatrix(matrix4x4: filterMatrix) |
| 84 | +} |
| 85 | + |
| 86 | +//#-end-editable-code |
| 87 | +/*: |
| 88 | +## Tone Equalization |
| 89 | + |
| 90 | +In this section we'll use a technique called **Tone Equalization** to solve the previous problem. This technique |
| 91 | +equalizes an image by *expanding light part to lightest and dark part to darkest*. For histogram's perspective, it |
| 92 | +*widens* a histogram to its maximum width. |
| 93 | + |
| 94 | +### 🔨Equalize the Images |
| 95 | + |
| 96 | +* Experiment: |
| 97 | + * In this experiment, we'll build a filter for **tone equalization**. |
| 98 | + * Try to read and complete the following code snippet. When you finish it, run your code and tap the **Equalization** |
| 99 | + button below the image to see whether it works. |
| 100 | +*/ |
| 101 | +//#-editable-code |
| 102 | +func applyGrayscaleFilterxx(rawImage: RawImage) { |
| 103 | + let coefficientRed: Float = 0.2126 |
| 104 | + let coefficientGreen: Float = 0.7152 |
| 105 | + let coefficientBlue: Float = 0.0722 |
| 106 | + var filterMatrix: [Float] = [ |
| 107 | + <#T##Red##Float#>, <#T##Red##Float#>, <#T##Red##Float#>, 0, |
| 108 | + <#T##Green##Float#>, <#T##Green##Float#>, <#T##Green##Float#>, 0, |
| 109 | + <#T##Blue##Float#>, <#T##Blue##Float#>, <#T##Blue##Float#>, 0, |
| 110 | + 0, 0, 0, 1 |
| 111 | + ] |
| 112 | + rawImage.multiplyByMatrix(matrix4x4: filterMatrix) |
| 113 | +} |
| 114 | + |
| 115 | +//#-end-editable-code |
| 116 | +/*: |
| 117 | +* Note: |
| 118 | + In this code snippet, we transform the image by multiplying it with a custom filter matrix. If you're not familiar |
| 119 | + with limier algebra, the following figure will explain how this transform matrix works. |
| 120 | +*/ |
| 121 | + |
| 122 | +//#-hidden-code |
| 123 | +let remoteView = remoteViewAsLiveViewProxy() |
| 124 | +let eventListener = EventListener(proxy: remoteView) { message in |
| 125 | + switch message { |
| 126 | + case .grayscaleFilterRequest(let enabled, let image): |
| 127 | + guard let rawImage = RawImage(uiImage: image) else { |
| 128 | + return |
| 129 | + } |
| 130 | + if enabled == true { |
| 131 | + applyGrayscaleFilter(rawImage: rawImage); |
| 132 | + } |
| 133 | + let destinationBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) |
| 134 | + if let destCGImage = rawImage.cgImage(bitmapInfo: destinationBitmapInfo), |
| 135 | + let destImage = try? UIImage(cgImage: destCGImage) { |
| 136 | + remoteView?.send(EventMessage.imageProcessingResponse(image: destImage).playgroundValue) |
| 137 | + } |
| 138 | + default: |
| 139 | + break |
| 140 | + } |
| 141 | +} |
8 | 142 | //#-end-hidden-code
|
9 |
| -let str = "Hello, playground" |
|
0 commit comments