[Swift]픽셀 데이터 다루기 2편 - Grayscale
[Swift]픽셀데이터 다루기 1편 - RGBA란?
RGBA?RGBA란 무엇일까? RGB는 우리가 흔히 아는 색상 표현 방식 중 하나이다.RGB는 3개의 숫자로 이루어져 있고 각 숫자는 0에서부터 255까지의 숫자이다.그리고 각 숫자는 순서대로 빨강, 초록, 파랑
rongios.tistory.com
주의) 여기서 다루는 내용은 실전에서 사용하는 방식을 사용하지 않음
1편에서는 UIImage로부터 픽셀 데이터를 추출하고 색상을 반전시키는 작업을 했다.
오늘은 아주 짧게 Grayscale을 적용하는 코드를 작성해볼까 한다.
시작하기 전 오늘도 나를 도와줄 이상해씨를 소개하고 시작하겠다.
먼저 그레이스케일을 어떻게 적용할까 고민을 해봤다.
내가 생각한 아이디어는 각 픽셀의 색상 RGB 값이 동일하면 무채색이 되니까 각 픽셀의 RGB 값을 기존 RGB 값의 평균으로 바꾸면 어떨까싶었다.
그렇게 구현한 메서드는 다음과 같다.
//PixelData 구조체의 메서드
struct PixelData {
let width: Int
let height: Int
let data: [UInt8]
func toUIImage() -> UIImage? { ... }
func grayscale(style: GrayscaleStyle) -> Self {
var modified: [UInt8] = []
switch style {
case .average:
modified = averageGrayscale(self.data)
}
return PixelData(width: width, height: height, data: modified)
}
private func averageGrayscale(_ data: [UInt8]) -> [UInt8] {
var result: [UInt8] = data
for i in stride(from: 0, to: data.count, by: 4) {
let average: UInt8 = UInt8((Double(data[i]) + Double(data[i+1]) + Double(data[i+2])) / 3)
result[i] = average
result[i+1] = average
result[i+2] = average
}
return result
}
// ...
enum GrayscaleStyle {
case average
}
}
이렇게 각 RGB 값을 단순하게 산술평균 내어 기존 값들을 대체해준다.
이걸 적용하면 어떻게 될까?
성공!!
하지만 여기서 끝이 아니다!
코드를 자세히 보면 GrayscaleStyle이라는 열거형이 구현되어 있다
이는 당연히 다른 스타일이 있다는 것을 암시하는 것!
평균값 적용을 구상해보고 검색을 해보니 Grayscale할 때 RGB 색상별로 가중치를 다르게 곱해서 더하는 방식이 있었다
바로 Luma 공식을 적용하는 것이다.
Luma 공식은 인간의 시각이 색상마다 반응하는 민감도가 다르다는 사실에 착안해서 RGB에 다른 가중치를 곱해서 휘도를 계산하는 방식이다.
가장 자주 쓰이는 공식은 RGB 기준으로 각각 0.299, 0.587, 0.114 가중치를 곱해주는 공식이다.
해당 가중치를 적용한 코드를 작성해 보았다.
struct PixelData {
let width: Int
let height: Int
let data: [UInt8]
func toUIImage() -> UIImage? { ... }
func grayscale(style: GrayscaleStyle) -> Self {
var modified: [UInt8] = []
switch style {
case .average:
modified = averageGrayscale(self.data)
case .luma:
modified = lumaGrayScale(self.data)
}
return PixelData(width: width, height: height, data: modified)
}
private func averageGrayscale(_ data: [UInt8]) -> [UInt8] { ... }
private func lumaGrayScale(_ data: [UInt8]) -> [UInt8] {
var result: [UInt8] = data
for i in stride(from: 0, to: data.count, by: 4) {
let luma = UInt8(0.299 * Double(data[i]) + 0.587 * Double(data[i+1]) + 0.114 * Double(data[i+2]))
result[i] = luma
result[i+1] = luma
result[i+2] = luma
}
return result
}
enum GrayscaleStyle {
case average
case luma
}
}
해당 코드를 통해 변환한 이미지와 기존 이미지를 한 번 비교해보자
평균 방식 | Luma 공식 적용 |
![]() |
![]() |
오호...
오른쪽이 조금 더 밝은(?) 느낌이 든다..
마무리
이렇게 두 가지 방식으로 그레이스케일을 적용해보고 실험해봤다!
사실 그 동안은 그레이스케일이 그냥 뭐 회색으로만 바꾸면 되는거지라고 생각했는데
인간의 색상 민감도 같은 부분을 고려해 가중치를 다르게 둔다는 것은 처음 알았다
이미지는 알면 알수록 재밌지만 어렵다...
소스코드
https://github.com/Kiyoung-Kim-57/PixelDataPractice
GitHub - Kiyoung-Kim-57/PixelDataPractice: 원본 픽셀 데이터를 다루는 실험 프로젝트
원본 픽셀 데이터를 다루는 실험 프로젝트. Contribute to Kiyoung-Kim-57/PixelDataPractice development by creating an account on GitHub.
github.com