이미지 오차 확산의 이치화~ 가마값 고려 Ver.+~
15672 단어 PythonnanopineoRaspberryPiPILoled
개요
오차 확산을 통해 이미지를 이치화하면 이미지가 밝아 보입니다.
가마를 고려하지 않아 생긴 현상이다.
최근 몇 년 동안 2치 디스플레이 유형의 OLED의 보급으로 인해 이미지를 예쁘게 보여주고 싶은 장면이 자주 발생했다.그래서 예뻐 보여야 한다.
(주요 보급 목적지는 라즈베리피, 비글본, 아르두노, 나노피)
그림% 1개의 캡션을 편집했습니다.
지붕
기존 문제
Python에서 PIL 라이브러리를 사용하면 오차 확산법으로 2치 이미지를 간단하게 생성할 수 있습니다.
예:image = Image.open(cover_path).convert('1')
하지만 두 가지 문제가 있다.
Python에서 PIL 라이브러리를 사용하면 오차 확산법으로 2치 이미지를 간단하게 생성할 수 있습니다.
예:
image = Image.open(cover_path).convert('1')
하지만 두 가지 문제가 있다.감마값을 고려하지 않기 때문에 오차의 확산이 밝아졌다.
대책 내용
float는 기본적으로 느리고 모두 고정 소수점의 정수 연산으로 실시한다.
(1) RGB → Grayscale 변환에서 Bt.709의 계수를 사용합니다.
Bt.709의 변환은 다음과 같습니다.
$ Luminance = R * 0.2126 + G * 0.7152 + B * 0.0722 $
이것은 65536을 곱하여 고정 소수점으로 처리한다.
이렇게 된다는 거야.
$ Luminance = (R * 13933 + G * 46871 + B * 4732)/65536 $
참고로 Bt.601은 아래와 같습니다.
OpenCV와 PIL의 Grayscale화는 이쪽 식이다.아마도 JPEG의 YUV-RGB 변환이 이렇기 때문일 것이다.
$ Luminance = R * 0.299 + G * 0.587 + B * 0.114 $
(2) 오차 확산이 선형 공간에서 실시
선형 공간으로 돌아가면 계산 오차가 확산됩니다.
선형 공간을 되돌릴 때 사용하는 가마 값은 sRGB에 대한 근사값 2.2를 사용하고 선형 공간 값은 고정된 소수점 20bit를 나타낸다.
즉, 가마값이 변환된 후 255는 1048575, 1은 5로 변한다.
그리고 1을 1로 비추는 것이 최저 조건이기 때문에 Gamma=2.2 시 18bit가 필요하다.
※ sRGB의 가마 곡선은 기계 설치를 간소화하기 위해 어두운 부분은 직선으로 다음과 같이 규격화됩니다.
www.color.org/srgb.pdfIf R, G, B are less than or equal to 0.04045
RL = R/12.92
GL = G/12.92
BL = B/12.02
If R, G, B are greater than 0.04045
RL = ((R + 0.055)/1.055)2.4
GL = ((R + 0.055)/1.055)2.4
BL = ((R + 0.055)/1.055)2.4
따라서 $(1/255)/12.92*4096=1.2432$이기 때문에 당분간 12bit이면 충분합니다.
모니터 같은 12bit LUT!!구가하는 것을 기다리는 것은 사실 최저 조건이다.
※ 위 규격서의 공식, 오류 3개도 있습니다.
(3) 오차 확산 FloydSteinberg 사용
FloydSteinberg
-
-
*
7/16
-
-
3/16
5/16
1/16
-
Python 버전 코드
PIL의 image를 입력하면 PIL의 image를 반환합니다.import math
from PIL import Image
def ImageHalftoning_FloydSteinberg(image):
shift = 20;
cx, cy = image.size;
temp = Image.new('I', (cx, cy));
result = Image.new('L', (cx, cy));
tmp = temp.load();
dst = result.load();
# Setup Gamma tablw
gamma = [0]*256;
for i in range(256):
gamma[i] = int( math.pow( i / 255.0, 2.2 ) * ((1 << shift) - 1) );
# Convert to initial value
if image.mode == 'L':
src = image.load();
for y in range(cy):
for x in range(cx):
tmp[(x,y)] = gamma[ src[(x,y)] ];
elif image.mode == 'RGB':
src = image.load();
for y in range(cy):
for x in range(cx):
R,G,B = src[(x,y)];
Y = (R * 13933 + G * 46871 + B * 4732) >> 16; # Bt.709
tmp[(x,y)] = gamma[ Y ];
elif image.mode == 'RGBA':
src = image.load();
for y in range(cy):
for x in range(cx):
R,G,B,A = src[(x,y)];
Y = (R * 13933 + G * 46871 + B * 4732) >> 16; # Bt.709
tmp[(x,y)] = gamma[ Y ];
else:
raise ValueError('Image.mode is not supported.')
# Error diffuse
for y in range(cy):
for x in range(cx):
c = tmp[(x,y)];
e = c if c < (1 << shift) else (c - ((1 << shift) - 1));
dst[(x,y)] = 0 if c < (1 << shift) else 255;
# FloydSteinberg
# - * 7/16
# 3/16 5/16 1/16
if (x+1) < cx :
tmp[(x+1,y)] += e * 7 / 16;
if (y+1) < cy :
if 0 <= (x-1) :
tmp[(x-1,y+1)] += e * 3 / 16;
tmp[(x,y+1)] += e * 5 / 16;
if (x+1) < cx :
tmp[(x+1,y+1)] += e * 1 / 16;
return result;
C++ 버전의 코드(RGB → L 변환 없음)는 다음 내용을 참조합니다.
NanoPi-NEO OpenCV Cap으로 OLED 디스플레이 - Qiita
마지막
이 Python 코드는 다음과 같은 내용을 적용하기 위해 제작되었습니다.
코드도git에 등록되었습니다.
NanoPi-NEO, MPD 및 OLED 음악 재생 서버 - Qiita
Reference
이 문제에 관하여(이미지 오차 확산의 이치화~ 가마값 고려 Ver.+~), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/blue-7/items/1d4f5c5b70777d829721
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
If R, G, B are less than or equal to 0.04045
RL = R/12.92
GL = G/12.92
BL = B/12.02
If R, G, B are greater than 0.04045
RL = ((R + 0.055)/1.055)2.4
GL = ((R + 0.055)/1.055)2.4
BL = ((R + 0.055)/1.055)2.4
import math
from PIL import Image
def ImageHalftoning_FloydSteinberg(image):
shift = 20;
cx, cy = image.size;
temp = Image.new('I', (cx, cy));
result = Image.new('L', (cx, cy));
tmp = temp.load();
dst = result.load();
# Setup Gamma tablw
gamma = [0]*256;
for i in range(256):
gamma[i] = int( math.pow( i / 255.0, 2.2 ) * ((1 << shift) - 1) );
# Convert to initial value
if image.mode == 'L':
src = image.load();
for y in range(cy):
for x in range(cx):
tmp[(x,y)] = gamma[ src[(x,y)] ];
elif image.mode == 'RGB':
src = image.load();
for y in range(cy):
for x in range(cx):
R,G,B = src[(x,y)];
Y = (R * 13933 + G * 46871 + B * 4732) >> 16; # Bt.709
tmp[(x,y)] = gamma[ Y ];
elif image.mode == 'RGBA':
src = image.load();
for y in range(cy):
for x in range(cx):
R,G,B,A = src[(x,y)];
Y = (R * 13933 + G * 46871 + B * 4732) >> 16; # Bt.709
tmp[(x,y)] = gamma[ Y ];
else:
raise ValueError('Image.mode is not supported.')
# Error diffuse
for y in range(cy):
for x in range(cx):
c = tmp[(x,y)];
e = c if c < (1 << shift) else (c - ((1 << shift) - 1));
dst[(x,y)] = 0 if c < (1 << shift) else 255;
# FloydSteinberg
# - * 7/16
# 3/16 5/16 1/16
if (x+1) < cx :
tmp[(x+1,y)] += e * 7 / 16;
if (y+1) < cy :
if 0 <= (x-1) :
tmp[(x-1,y+1)] += e * 3 / 16;
tmp[(x,y+1)] += e * 5 / 16;
if (x+1) < cx :
tmp[(x+1,y+1)] += e * 1 / 16;
return result;
이 Python 코드는 다음과 같은 내용을 적용하기 위해 제작되었습니다.
코드도git에 등록되었습니다.
NanoPi-NEO, MPD 및 OLED 음악 재생 서버 - Qiita
Reference
이 문제에 관하여(이미지 오차 확산의 이치화~ 가마값 고려 Ver.+~), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/blue-7/items/1d4f5c5b70777d829721텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)