mirror of
https://github.com/dschmenk/SHR-NTSC.git
synced 2024-12-26 02:30:18 +00:00
Initial check-in
This commit is contained in:
parent
e9d8fafd41
commit
0dcc35b7d9
36
makefile
Executable file
36
makefile
Executable file
@ -0,0 +1,36 @@
|
||||
.SUFFIXES =
|
||||
AFLAGS = -o $@
|
||||
IMGVIEW = IMGVIEW.SYSTEM\#FF2000
|
||||
#
|
||||
# Image filetypes for Virtual ][
|
||||
#
|
||||
PLATYPE = .\$$ED
|
||||
BINTYPE = .BIN
|
||||
SYSTYPE = .SYS
|
||||
TXTTYPE = .TXT
|
||||
#
|
||||
# Image filetypes for CiderPress
|
||||
#
|
||||
#RELTYPE = \#FE1000
|
||||
#INTERPTYPE = \#050000
|
||||
#BINTYPE = \#060000
|
||||
#SYSTYPE = \#FF2000
|
||||
#TXTTYPE = \#040000
|
||||
|
||||
all: $(IMGVIEW)
|
||||
bin: imgview.bin
|
||||
|
||||
clean:
|
||||
-rm *.o *~ *.a *.bin
|
||||
|
||||
image.asm: ntsc.py
|
||||
python ntsc.py > image.asm
|
||||
|
||||
$(IMGVIEW): imgview.asm image.asm
|
||||
acme --setpc 8192 -o $(IMGVIEW) imgview.asm
|
||||
ac -d IMGVIEW.PO IMGVIEW.SYSTEM
|
||||
ac -p IMGVIEW.PO IMGVIEW.SYSTEM SYS < IMGVIEW.SYSTEM#FF2000
|
||||
|
||||
imgview.bin: imgview.asm image.asm
|
||||
acme --setpc 4096 -o imgview.bin imgview.asm
|
||||
|
169
ntsc-140.py
Normal file
169
ntsc-140.py
Normal file
@ -0,0 +1,169 @@
|
||||
import pygame, math, sys
|
||||
from pygame.locals import *
|
||||
from PIL import Image
|
||||
|
||||
WIDTH = 140
|
||||
HEIGHT = 200
|
||||
DEG_PER_CIRCLE = 360
|
||||
DEG_TO_RAD = math.pi*2/DEG_PER_CIRCLE
|
||||
UMAX = 0.436
|
||||
VMAX = 0.615
|
||||
|
||||
ntscRGB = [[(0, 0, 0) for y in xrange(4)] for p in xrange(32)]
|
||||
ntscPixel = [[(0, 0, 0)] for m in xrange(7)]
|
||||
ntscRange = [0, 4, 9, 13, 18, 23, 27]
|
||||
shrPixels = []
|
||||
ntscOutput = []
|
||||
|
||||
def yuv2rgb(y, u, v):
|
||||
u *= UMAX
|
||||
v *= VMAX
|
||||
r = max(0.0, y + 1.14 * v)
|
||||
g = max(0.0, y - 0.395 * u - 0.581 * v)
|
||||
b = max(0.0, y + 2.033 * u)
|
||||
return (r, g, b)
|
||||
|
||||
def luv2rgb(y, u, v):
|
||||
r = max(0.0, y + v)
|
||||
g = max(0.0, y - 0.707 * u - 0.707 * v)
|
||||
b = max(0.0, y + u)
|
||||
return (r, g, b)
|
||||
|
||||
def ntscInitRGB(angle):
|
||||
YScale = [0.0, 0.25, 0.50, 1.0]#[0.0, 0.3334, 0.6667, 1.0]
|
||||
redSum = 0.0
|
||||
grnSum = 0.0
|
||||
bluSum = 0.0
|
||||
rgb = []
|
||||
for pix in xrange(32):
|
||||
#
|
||||
# U, V for this chroma angle
|
||||
#
|
||||
u = math.cos(angle * DEG_TO_RAD)
|
||||
v = math.sin(angle * DEG_TO_RAD)
|
||||
#
|
||||
# Calculate and NTSC RGB for this SHR pixel
|
||||
#
|
||||
red, grn, blu = luv2rgb(1.0, u, v)
|
||||
redSum += red
|
||||
grnSum += grn
|
||||
bluSum += blu
|
||||
rgb.append((red, grn, blu))
|
||||
#
|
||||
# Next NTSC chroma pixel
|
||||
#
|
||||
angle = angle - 78.75
|
||||
if angle > 360.0:
|
||||
angle -= 360.0
|
||||
if angle < 0.0:
|
||||
angle += 360.0
|
||||
#
|
||||
# Normalize the RGB values of each NTSC pixel component so they add up to white
|
||||
#
|
||||
redScale = 255.0 * 7.0 / redSum
|
||||
grnScale = 255.0 * 7.0 / grnSum
|
||||
bluScale = 255.0 * 7.0 / bluSum
|
||||
for lum in xrange(4):
|
||||
for pix in xrange(len(rgb)):
|
||||
ntscRGB[pix][lum] = (min(255,int(rgb[pix][0]*redScale*YScale[lum])), min(255,int(rgb[pix][1]*grnScale*YScale[lum])), min(255,int(rgb[pix][2]*bluScale*YScale[lum])))
|
||||
|
||||
def lumInc(seq):
|
||||
s = 0
|
||||
seq[s] += 1
|
||||
while seq[s] == 4:
|
||||
seq[s] = 0
|
||||
s += 1
|
||||
if s == len(seq):
|
||||
return False
|
||||
seq[s] += 1
|
||||
return True
|
||||
|
||||
def ntscInitPixels():
|
||||
#
|
||||
# NTSC pixel mapping to SHR range
|
||||
#
|
||||
for pix7 in xrange(7):
|
||||
shrpix = [0 for p in xrange(5)]
|
||||
while lumInc(shrpix):
|
||||
red = 0
|
||||
grn = 0
|
||||
blu = 0
|
||||
for s in xrange(len(shrpix)):
|
||||
red += ntscRGB[ntscRange[pix7]+s][shrpix[s]][0]
|
||||
grn += ntscRGB[ntscRange[pix7]+s][shrpix[s]][1]
|
||||
blu += ntscRGB[ntscRange[pix7]+s][shrpix[s]][2]
|
||||
ntscPixel[pix7].append((min(255,int(red * 0.914)), min(255,int(grn * 0.914)), min(255,int(blu * 0.914)))) # 0.914 = 5 / 4.57
|
||||
|
||||
def ntscMapRGB(rgb, x):
|
||||
global shrPixels
|
||||
|
||||
nearest = 195075
|
||||
nBest = 0
|
||||
errRed = 0
|
||||
errGrn = 0
|
||||
errBlu = 0
|
||||
pix7 = x % 7
|
||||
if pix7 == 0:
|
||||
shrPixels = [0 for n in xrange(32)]
|
||||
#
|
||||
# Look for best RGB match
|
||||
#
|
||||
redMatch = rgb[0]
|
||||
grnMatch = rgb[1]
|
||||
bluMatch = rgb[2]
|
||||
for n in xrange(len(ntscPixel[pix7])):
|
||||
red = redMatch - ntscPixel[pix7][n][0]
|
||||
grn = grnMatch - ntscPixel[pix7][n][1]
|
||||
blu = bluMatch - ntscPixel[pix7][n][2]
|
||||
dist = red*red + grn*grn + blu*blu
|
||||
if dist < nearest:
|
||||
nearest = dist
|
||||
nBest = n
|
||||
errRed = red
|
||||
errGrn = grn
|
||||
errBlu = blu
|
||||
rgbBest = ntscPixel[pix7][nBest]
|
||||
#
|
||||
# Convert to SHR pixels
|
||||
#
|
||||
for n in xrange(5):
|
||||
shrPixels[ntscRange[pix7] + n] = nBest & 0x03
|
||||
nBest >>= 2
|
||||
#
|
||||
# Output SHR pixels for assembly
|
||||
#
|
||||
if pix7 == 6:
|
||||
print '\t!BYTE\t$%02X, ' % ((shrPixels[0] << 6) | (shrPixels[1] << 4) | (shrPixels[2] << 2) | shrPixels[3]),
|
||||
print '$%02X, ' % ((shrPixels[4] << 6) | (shrPixels[5] << 4) | (shrPixels[6] << 2) | shrPixels[7]),
|
||||
print '$%02X, ' % ((shrPixels[8] << 6) | (shrPixels[9] << 4) | (shrPixels[10] << 2) | shrPixels[11]),
|
||||
print '$%02X, ' % ((shrPixels[12] << 6) | (shrPixels[13] << 4) | (shrPixels[14] << 2) | shrPixels[15]),
|
||||
print '$%02X, ' % ((shrPixels[16] << 6) | (shrPixels[17] << 4) | (shrPixels[18] << 2) | shrPixels[19]),
|
||||
print '$%02X, ' % ((shrPixels[20] << 6) | (shrPixels[21] << 4) | (shrPixels[22] << 2) | shrPixels[23]),
|
||||
print '$%02X, ' % ((shrPixels[24] << 6) | (shrPixels[25] << 4) | (shrPixels[26] << 2) | shrPixels[27]),
|
||||
print '$%02X' % ((shrPixels[28] << 6) | (shrPixels[29] << 4) | (shrPixels[30] << 2) | shrPixels[31])
|
||||
return rgbBest
|
||||
|
||||
def displayBars():
|
||||
for bar in xrange(32):
|
||||
for lum in xrange(4):
|
||||
surface.fill(ntscRGB[bar][lum], pygame.Rect(bar*4.57, lum*50, 5, 50))
|
||||
pygame.display.flip()
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
imagefile = sys.argv[1]
|
||||
else:
|
||||
imagefile = "image.jpg"
|
||||
image = Image.open(imagefile)
|
||||
if image.size[0] <> WIDTH or image.size[1] <> HEIGHT:
|
||||
image = image.resize((WIDTH, HEIGHT), Image.ANTIALIAS)
|
||||
pygame.init()
|
||||
surface = pygame.display.set_mode((WIDTH,HEIGHT))
|
||||
surfpix = pygame.PixelArray(surface)
|
||||
ntscInitRGB(103.0)
|
||||
ntscInitPixels()
|
||||
displayBars()
|
||||
for y in xrange(HEIGHT):
|
||||
print '; scanline: ', y
|
||||
for x in xrange(WIDTH):
|
||||
surfpix[x][y] = ntscMapRGB(image.getpixel((x, y)), x)
|
||||
pygame.display.flip()
|
174
ntsc-160.py
Normal file
174
ntsc-160.py
Normal file
@ -0,0 +1,174 @@
|
||||
import pygame, math, sys
|
||||
from pygame.locals import *
|
||||
from PIL import Image
|
||||
|
||||
WIDTH = 160
|
||||
HEIGHT = 200
|
||||
DEG_PER_CIRCLE = 360
|
||||
DEG_TO_RAD = math.pi*2/DEG_PER_CIRCLE
|
||||
UMAX = 0.436
|
||||
VMAX = 0.615
|
||||
|
||||
ntscRGB = [[(0, 0, 0) for y in xrange(4)] for p in xrange(32)]
|
||||
ntscPixel = [[(0, 0, 0)] for m in xrange(8)]
|
||||
shrPixels = []
|
||||
ntscPrev = (0, 0, 0)
|
||||
|
||||
def yuv2rgb(y, u, v):
|
||||
u *= UMAX
|
||||
v *= VMAX
|
||||
r = max(0.0, y + 1.14 * v)
|
||||
g = max(0.0, y - 0.395 * u - 0.581 * v)
|
||||
b = max(0.0, y + 2.033 * u)
|
||||
return (r, g, b)
|
||||
|
||||
def luv2rgb(y, u, v):
|
||||
r = max(0.0, y + v)
|
||||
g = max(0.0, y - 0.707 * u - 0.707 * v)
|
||||
b = max(0.0, y + u)
|
||||
return (r, g, b)
|
||||
|
||||
def ntscInitRGB(angle):
|
||||
YScale = [0.0, 0.25, 0.50, 1.0]#[0.0, 0.3334, 0.6667, 1.0]
|
||||
redSum = 0.0
|
||||
grnSum = 0.0
|
||||
bluSum = 0.0
|
||||
rgb = []
|
||||
for pix in xrange(32):
|
||||
#
|
||||
# U, V for this chroma angle
|
||||
#
|
||||
u = math.cos(angle * DEG_TO_RAD)
|
||||
v = math.sin(angle * DEG_TO_RAD)
|
||||
#
|
||||
# Calculate and NTSC RGB for this SHR pixel
|
||||
#
|
||||
red, grn, blu = luv2rgb(1.0, u, v)
|
||||
redSum += red
|
||||
grnSum += grn
|
||||
bluSum += blu
|
||||
rgb.append((red, grn, blu))
|
||||
#
|
||||
# Next NTSC chroma pixel
|
||||
#
|
||||
angle = angle - 78.75
|
||||
if angle > 360.0:
|
||||
angle -= 360.0
|
||||
if angle < 0.0:
|
||||
angle += 360.0
|
||||
#
|
||||
# Normalize the RGB values of each NTSC pixel component so they add up to white
|
||||
#
|
||||
redScale = 255.0 * 7.0 / redSum
|
||||
grnScale = 255.0 * 7.0 / grnSum
|
||||
bluScale = 255.0 * 7.0 / bluSum
|
||||
for lum in xrange(4):
|
||||
for pix in xrange(len(rgb)):
|
||||
ntscRGB[pix][lum] = (min(255,int(rgb[pix][0]*redScale*YScale[lum])), min(255,int(rgb[pix][1]*grnScale*YScale[lum])), min(255,int(rgb[pix][2]*bluScale*YScale[lum])))
|
||||
|
||||
def lumInc(seq):
|
||||
s = 0
|
||||
seq[s] += 1
|
||||
while seq[s] == 4:
|
||||
seq[s] = 0
|
||||
s += 1
|
||||
if s == len(seq):
|
||||
return False
|
||||
seq[s] += 1
|
||||
return True
|
||||
|
||||
def ntscInitPixels():
|
||||
#
|
||||
# NTSC pixel mapping to SHR range
|
||||
#
|
||||
for pix8 in xrange(8):
|
||||
shrpix = [0 for p in xrange(4)]
|
||||
while lumInc(shrpix):
|
||||
red = 0
|
||||
grn = 0
|
||||
blu = 0
|
||||
for s in xrange(len(shrpix)):
|
||||
red += ntscRGB[pix8*4+s][shrpix[s]][0]
|
||||
grn += ntscRGB[pix8*4+s][shrpix[s]][1]
|
||||
blu += ntscRGB[pix8*4+s][shrpix[s]][2]
|
||||
ntscPixel[pix8].append((red, grn, blu))
|
||||
|
||||
def ntscMapRGB(rgb, x):
|
||||
global shrPixels
|
||||
global ntscPrev
|
||||
|
||||
pix8 = x % 8
|
||||
if pix8 == 0:
|
||||
shrPixels = [0 for n in xrange(32)]
|
||||
#
|
||||
# Adjust source color based on reduced destination precision
|
||||
#
|
||||
redMatch = max(0,int(rgb[0] - ntscPrev[0] * 0.57))
|
||||
grnMatch = max(0,int(rgb[1] - ntscPrev[1] * 0.57))
|
||||
bluMatch = max(0,int(rgb[2] - ntscPrev[2] * 0.57))
|
||||
#
|
||||
# Look for best RGB match
|
||||
#
|
||||
nearest = 195075
|
||||
nBest = 0
|
||||
for n in xrange(len(ntscPixel[pix8])):
|
||||
red = redMatch - ntscPixel[pix8][n][0]
|
||||
grn = grnMatch - ntscPixel[pix8][n][1]
|
||||
blu = bluMatch - ntscPixel[pix8][n][2]
|
||||
dist = red*red + grn*grn + blu*blu
|
||||
if dist < nearest:
|
||||
nearest = dist
|
||||
nBest = n
|
||||
#
|
||||
# Update ouput list
|
||||
#
|
||||
rgbBest = ntscPixel[pix8][nBest]
|
||||
#
|
||||
# Convert to SHR pixels
|
||||
#
|
||||
for n in xrange(4):
|
||||
shr = nBest & 0x03
|
||||
shrPixels[pix8*4 + n] = shr
|
||||
nBest >>= 2
|
||||
#
|
||||
# Save last pixel
|
||||
#
|
||||
ntscPrev = ntscRGB[(x*4 + 3) & 0x1F][shr]
|
||||
#
|
||||
# Output SHR pixels for assembly
|
||||
#
|
||||
if pix8 == 7:
|
||||
print '\t!BYTE\t$%02X, ' % ((shrPixels[0] << 6) | (shrPixels[1] << 4) | (shrPixels[2] << 2) | shrPixels[3]),
|
||||
print '$%02X, ' % ((shrPixels[4] << 6) | (shrPixels[5] << 4) | (shrPixels[6] << 2) | shrPixels[7]),
|
||||
print '$%02X, ' % ((shrPixels[8] << 6) | (shrPixels[9] << 4) | (shrPixels[10] << 2) | shrPixels[11]),
|
||||
print '$%02X, ' % ((shrPixels[12] << 6) | (shrPixels[13] << 4) | (shrPixels[14] << 2) | shrPixels[15]),
|
||||
print '$%02X, ' % ((shrPixels[16] << 6) | (shrPixels[17] << 4) | (shrPixels[18] << 2) | shrPixels[19]),
|
||||
print '$%02X, ' % ((shrPixels[20] << 6) | (shrPixels[21] << 4) | (shrPixels[22] << 2) | shrPixels[23]),
|
||||
print '$%02X, ' % ((shrPixels[24] << 6) | (shrPixels[25] << 4) | (shrPixels[26] << 2) | shrPixels[27]),
|
||||
print '$%02X' % ((shrPixels[28] << 6) | (shrPixels[29] << 4) | (shrPixels[30] << 2) | shrPixels[31])
|
||||
return rgbBest
|
||||
|
||||
def displayBars():
|
||||
for bar in xrange(32):
|
||||
for lum in xrange(4):
|
||||
surface.fill(ntscRGB[bar][lum], pygame.Rect(bar * 5, lum*50, 5, 50))
|
||||
pygame.display.flip()
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
imagefile = sys.argv[1]
|
||||
else:
|
||||
imagefile = "image.jpg"
|
||||
image = Image.open(imagefile)
|
||||
if image.size[0] <> WIDTH or image.size[1] <> HEIGHT:
|
||||
image = image.resize((WIDTH, HEIGHT), Image.ANTIALIAS)
|
||||
pygame.init()
|
||||
surface = pygame.display.set_mode((WIDTH,HEIGHT))
|
||||
surfpix = pygame.PixelArray(surface)
|
||||
ntscInitRGB(103.0)
|
||||
ntscInitPixels()
|
||||
displayBars()
|
||||
for y in xrange(HEIGHT):
|
||||
print '; scanline: ', y
|
||||
for x in xrange(WIDTH):
|
||||
surfpix[x][y] = ntscMapRGB(image.getpixel((x, y)), x)
|
||||
pygame.display.flip()
|
168
ntsc-640-err.py
Executable file
168
ntsc-640-err.py
Executable file
@ -0,0 +1,168 @@
|
||||
import pygame, math, sys
|
||||
from pygame.locals import *
|
||||
from PIL import Image
|
||||
from collections import deque
|
||||
|
||||
WIDTH = 640
|
||||
HEIGHT = 200
|
||||
DEG_PER_CIRCLE = 360
|
||||
DEG_TO_RAD = math.pi*2/DEG_PER_CIRCLE
|
||||
UMAX = 0.436
|
||||
VMAX = 0.615
|
||||
|
||||
ntscRGB = [[(0, 0, 0) for y in xrange(4)] for p in xrange(32)]
|
||||
ntscErr = [(0, 0, 0) for p in xrange(WIDTH)]
|
||||
ntscOutput = []
|
||||
|
||||
def yuv2rgb(y, u, v):
|
||||
u *= UMAX
|
||||
v *= VMAX
|
||||
r = max(0.0, y + 1.14 * v)
|
||||
g = max(0.0, y - 0.395 * u - 0.581 * v)
|
||||
b = max(0.0, y + 2.033 * u)
|
||||
return r, g, b
|
||||
|
||||
def luv2rgb(y, u, v):
|
||||
r = max(0.0, y + v)
|
||||
g = max(0.0, y - 0.707 * u - 0.707 * v)
|
||||
b = max(0.0, y + u)
|
||||
return r, g, b
|
||||
|
||||
def ntscInitRGB(angle):
|
||||
YScale = [0.0, 0.25, 0.50, 1.0]#[0.0, 0.3333, 0.6667, 1.0]
|
||||
redSum = 0.0
|
||||
grnSum = 0.0
|
||||
bluSum = 0.0
|
||||
rgb = []
|
||||
for pix in xrange(32):
|
||||
#
|
||||
# U, V for this chroma angle
|
||||
#
|
||||
u = math.cos(angle * DEG_TO_RAD)
|
||||
v = math.sin(angle * DEG_TO_RAD)
|
||||
#
|
||||
# Calculate and NTSC RGB for this SHR pixel
|
||||
#
|
||||
red, grn, blu = luv2rgb(1.0, u, v)
|
||||
redSum += red
|
||||
grnSum += grn
|
||||
bluSum += blu
|
||||
rgb.append((red, grn, blu))
|
||||
#
|
||||
# Next NTSC chroma pixel
|
||||
#
|
||||
angle = angle - 78.75
|
||||
if angle > 360.0:
|
||||
angle -= 360.0
|
||||
if angle < 0.0:
|
||||
angle += 360.0
|
||||
#
|
||||
# Normalize the RGB values of each NTSC pixel component so they add up to white
|
||||
#
|
||||
redScale = 255.0 * 7.0 / redSum
|
||||
grnScale = 255.0 * 7.0 / grnSum
|
||||
bluScale = 255.0 * 7.0 / bluSum
|
||||
for lum in xrange(4):
|
||||
for pix in xrange(len(rgb)):
|
||||
ntscRGB[pix][lum] = (min(255,int(rgb[pix][0]*redScale*YScale[lum])), min(255,int(rgb[pix][1]*grnScale*YScale[lum])), min(255,int(rgb[pix][2]*bluScale*YScale[lum])))
|
||||
|
||||
def ntscInitPrev():
|
||||
global ntscOutput
|
||||
ntscOutput = deque([(128, 128, 128) for p in xrange(4)])
|
||||
|
||||
def ntscPrev():
|
||||
#
|
||||
# Return previous NTSC chroma cycle output
|
||||
#
|
||||
l = len(ntscOutput)
|
||||
red = ntscOutput[0][0] * 0.57
|
||||
grn = ntscOutput[0][1] * 0.57
|
||||
blu = ntscOutput[0][2] * 0.57
|
||||
for p in xrange(1, l):
|
||||
red += ntscOutput[p][0]
|
||||
grn += ntscOutput[p][1]
|
||||
blu += ntscOutput[p][2]
|
||||
return (min(255,red), min(255,grn), min(255,blu))
|
||||
|
||||
def ntscBest(ntsc, rgb):
|
||||
nearest = 195075
|
||||
lumBest = 0
|
||||
for lum in xrange(4):
|
||||
red = rgb[0] - ntsc[lum][0]
|
||||
grn = rgb[1] - ntsc[lum][1]
|
||||
blu = rgb[2] - ntsc[lum][2]
|
||||
dist = red*red + grn*grn + blu*blu
|
||||
if dist < nearest:
|
||||
nearest = dist
|
||||
lumBest = lum
|
||||
#
|
||||
# Update ouput list
|
||||
#
|
||||
ntscOutput.popleft()
|
||||
ntscOutput.append(ntsc[lumBest])
|
||||
return lumBest
|
||||
|
||||
def ntscMapRGB(rgb, err, x):
|
||||
shr = []
|
||||
redErr = err[0]
|
||||
grnErr = err[1]
|
||||
bluErr = err[2]
|
||||
for p in xrange(len(rgb)):
|
||||
redErr += ntscErr[x + p][0]
|
||||
grnErr += ntscErr[x + p][1]
|
||||
bluErr += ntscErr[x + p][2]
|
||||
prev = ntscPrev()
|
||||
red = min(255,max(0, rgb[p][0] + redErr - prev[0]))
|
||||
grn = min(255,max(0, rgb[p][1] + grnErr - prev[1]))
|
||||
blu = min(255,max(0, rgb[p][2] + bluErr - prev[2]))
|
||||
shr.append(ntscBest(ntscRGB[p], (red, grn, blu)))
|
||||
redErr = int((red - ntscRGB[p][shr[p]][0]) / 4.57 / 1.0)
|
||||
grnErr = int((grn - ntscRGB[p][shr[p]][1]) / 4.57 / 1.0)
|
||||
bluErr = int((blu - ntscRGB[p][shr[p]][2]) / 4.57 / 1.0)
|
||||
ntscErr[x + p] = (redErr, grnErr, bluErr)
|
||||
return shr, (redErr, grnErr, bluErr)
|
||||
|
||||
def displayBars():
|
||||
for l in xrange(4):
|
||||
for bar in xrange(len(ntscRGB)):
|
||||
surface.fill(ntscRGB[bar][l], pygame.Rect(bar * 20, l * 50, 20, 50))
|
||||
pygame.display.flip()
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
imagefile = sys.argv[1]
|
||||
else:
|
||||
imagefile = "image.jpg"
|
||||
image = Image.open(imagefile)
|
||||
if image.size[0] <> WIDTH or image.size[1] <> HEIGHT:
|
||||
image = image.resize((WIDTH, HEIGHT), Image.ANTIALIAS)
|
||||
pygame.init()
|
||||
surface = pygame.display.set_mode((WIDTH,HEIGHT))
|
||||
surfpix = pygame.PixelArray(surface)
|
||||
ntscInitRGB(103.0)
|
||||
ntscInitPrev()
|
||||
displayBars()
|
||||
for y in xrange(HEIGHT):
|
||||
print '; scanline: ', y
|
||||
errRight = (0, 0, 0)
|
||||
for x in xrange(0, WIDTH, len(ntscRGB)):
|
||||
rgb = []
|
||||
for p in xrange(len(ntscRGB)):
|
||||
rgb.append(image.getpixel((x + p, y)))
|
||||
shr, errRight = ntscMapRGB(rgb, errRight, x)
|
||||
#
|
||||
# Copy displayable SHR pixels
|
||||
#
|
||||
for p in xrange(len(shr)):
|
||||
surfpix[p + x][y] = (ntscRGB[p][shr[p]][0], ntscRGB[p][shr[p]][1], ntscRGB[p][shr[p]][2])
|
||||
#
|
||||
# Output SHR pixels for assembly
|
||||
#
|
||||
print '\t!BYTE\t$%02X, ' % ((shr[0] << 6) | (shr[1] << 4) | (shr[2] << 2) | shr[3]),
|
||||
print '$%02X, ' % ((shr[4] << 6) | (shr[5] << 4) | (shr[6] << 2) | shr[7]),
|
||||
print '$%02X, ' % ((shr[8] << 6) | (shr[9] << 4) | (shr[10] << 2) | shr[11]),
|
||||
print '$%02X, ' % ((shr[12] << 6) | (shr[13] << 4) | (shr[14] << 2) | shr[15]),
|
||||
print '$%02X, ' % ((shr[16] << 6) | (shr[17] << 4) | (shr[18] << 2) | shr[19]),
|
||||
print '$%02X, ' % ((shr[20] << 6) | (shr[21] << 4) | (shr[22] << 2) | shr[23]),
|
||||
print '$%02X, ' % ((shr[24] << 6) | (shr[25] << 4) | (shr[26] << 2) | shr[27]),
|
||||
print '$%02X' % ((shr[28] << 6) | (shr[29] << 4) | (shr[30] << 2) | shr[31])
|
||||
pygame.display.flip()
|
156
ntsc-640.py
Normal file
156
ntsc-640.py
Normal file
@ -0,0 +1,156 @@
|
||||
import pygame, math, sys
|
||||
from pygame.locals import *
|
||||
from PIL import Image
|
||||
from collections import deque
|
||||
|
||||
WIDTH = 640
|
||||
HEIGHT = 200
|
||||
DEG_PER_CIRCLE = 360
|
||||
DEG_TO_RAD = math.pi*2/DEG_PER_CIRCLE
|
||||
UMAX = 0.436
|
||||
VMAX = 0.615
|
||||
|
||||
ntscRGB = [[(0, 0, 0) for y in xrange(4)] for p in xrange(32)]
|
||||
ntscOutput = []
|
||||
|
||||
def yuv2rgb(y, u, v):
|
||||
u *= UMAX
|
||||
v *= VMAX
|
||||
r = max(0.0, y + 1.14 * v)
|
||||
g = max(0.0, y - 0.395 * u - 0.581 * v)
|
||||
b = max(0.0, y + 2.033 * u)
|
||||
return r, g, b
|
||||
|
||||
def luv2rgb(y, u, v):
|
||||
r = max(0.0, y + v)
|
||||
g = max(0.0, y - 0.707 * u - 0.707 * v)
|
||||
b = max(0.0, y + u)
|
||||
return r, g, b
|
||||
|
||||
def ntscInitRGB(angle):
|
||||
YScale = [0.0, 0.25, 0.50, 1.0]#[0.0, 0.3333, 0.6667, 1.0]
|
||||
redSum = 0.0
|
||||
grnSum = 0.0
|
||||
bluSum = 0.0
|
||||
rgb = []
|
||||
for pix in xrange(32):
|
||||
#
|
||||
# U, V for this chroma angle
|
||||
#
|
||||
u = math.cos(angle * DEG_TO_RAD)
|
||||
v = math.sin(angle * DEG_TO_RAD)
|
||||
#
|
||||
# Calculate and NTSC RGB for this SHR pixel
|
||||
#
|
||||
red, grn, blu = luv2rgb(1.0, u, v)
|
||||
redSum += red
|
||||
grnSum += grn
|
||||
bluSum += blu
|
||||
rgb.append((red, grn, blu))
|
||||
#
|
||||
# Next NTSC chroma pixel
|
||||
#
|
||||
angle = angle - 78.75
|
||||
if angle > 360.0:
|
||||
angle -= 360.0
|
||||
if angle < 0.0:
|
||||
angle += 360.0
|
||||
#
|
||||
# Normalize the RGB values of each NTSC pixel component so they add up to white
|
||||
#
|
||||
redScale = 255.0 * 7.0 / redSum
|
||||
grnScale = 255.0 * 7.0 / grnSum
|
||||
bluScale = 255.0 * 7.0 / bluSum
|
||||
for lum in xrange(4):
|
||||
for pix in xrange(len(rgb)):
|
||||
ntscRGB[pix][lum] = (min(255,int(rgb[pix][0]*redScale*YScale[lum])), min(255,int(rgb[pix][1]*grnScale*YScale[lum])), min(255,int(rgb[pix][2]*bluScale*YScale[lum])))
|
||||
|
||||
def ntscInitPrev():
|
||||
global ntscOutput
|
||||
ntscOutput = deque([(128, 128, 128) for p in xrange(4)])
|
||||
|
||||
def ntscPrev():
|
||||
#
|
||||
# Return previous NTSC chroma cycle output
|
||||
#
|
||||
l = len(ntscOutput)
|
||||
red = ntscOutput[0][0] * 0.57
|
||||
grn = ntscOutput[0][1] * 0.57
|
||||
blu = ntscOutput[0][2] * 0.57
|
||||
for p in xrange(1, l):
|
||||
red += ntscOutput[p][0]
|
||||
grn += ntscOutput[p][1]
|
||||
blu += ntscOutput[p][2]
|
||||
return (min(255,red), min(255,grn), min(255,blu))
|
||||
|
||||
def ntscBest(ntsc, rgb):
|
||||
nearest = 195075
|
||||
lumBest = 0
|
||||
for lum in xrange(4):
|
||||
red = rgb[0] - ntsc[lum][0]
|
||||
grn = rgb[1] - ntsc[lum][1]
|
||||
blu = rgb[2] - ntsc[lum][2]
|
||||
dist = red*red + grn*grn + blu*blu
|
||||
if dist < nearest:
|
||||
nearest = dist
|
||||
lumBest = lum
|
||||
#
|
||||
# Update ouput list
|
||||
#
|
||||
ntscOutput.popleft()
|
||||
ntscOutput.append(ntsc[lumBest])
|
||||
return lumBest
|
||||
|
||||
def ntscMapRGB(rgb):
|
||||
shr = []
|
||||
for p in xrange(len(rgb)):
|
||||
prev = ntscPrev()
|
||||
red = max(0, rgb[p][0] - prev[0])
|
||||
grn = max(0, rgb[p][1] - prev[1])
|
||||
blu = max(0, rgb[p][2] - prev[2])
|
||||
shr.append(ntscBest(ntscRGB[p], (red, grn, blu)))
|
||||
return shr
|
||||
|
||||
def displayBars():
|
||||
for l in xrange(4):
|
||||
for bar in xrange(len(ntscRGB)):
|
||||
surface.fill(ntscRGB[bar][l], pygame.Rect(bar * 20, l * 50, 20, 50))
|
||||
pygame.display.flip()
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
imagefile = sys.argv[1]
|
||||
else:
|
||||
imagefile = "image.jpg"
|
||||
image = Image.open(imagefile)
|
||||
if image.size[0] <> WIDTH or image.size[1] <> HEIGHT:
|
||||
image = image.resize((WIDTH, HEIGHT), Image.ANTIALIAS)
|
||||
pygame.init()
|
||||
surface = pygame.display.set_mode((WIDTH,HEIGHT))
|
||||
surfpix = pygame.PixelArray(surface)
|
||||
ntscInitRGB(103.0)
|
||||
ntscInitPrev()
|
||||
displayBars()
|
||||
for y in xrange(HEIGHT):
|
||||
print '; scanline: ', y
|
||||
for x in xrange(0, WIDTH, len(ntscRGB)):
|
||||
rgb = []
|
||||
for p in xrange(len(ntscRGB)):
|
||||
rgb.append(image.getpixel((x + p, y)))
|
||||
shr = ntscMapRGB(rgb)
|
||||
#
|
||||
# Copy displayable SHR pixels
|
||||
#
|
||||
for p in xrange(len(shr)):
|
||||
surfpix[p + x][y] = (ntscRGB[p][shr[p]][0], ntscRGB[p][shr[p]][1], ntscRGB[p][shr[p]][2])
|
||||
#
|
||||
# Output SHR pixels for assembly
|
||||
#
|
||||
print '\t!BYTE\t$%02X, ' % ((shr[0] << 6) | (shr[1] << 4) | (shr[2] << 2) | shr[3]),
|
||||
print '$%02X, ' % ((shr[4] << 6) | (shr[5] << 4) | (shr[6] << 2) | shr[7]),
|
||||
print '$%02X, ' % ((shr[8] << 6) | (shr[9] << 4) | (shr[10] << 2) | shr[11]),
|
||||
print '$%02X, ' % ((shr[12] << 6) | (shr[13] << 4) | (shr[14] << 2) | shr[15]),
|
||||
print '$%02X, ' % ((shr[16] << 6) | (shr[17] << 4) | (shr[18] << 2) | shr[19]),
|
||||
print '$%02X, ' % ((shr[20] << 6) | (shr[21] << 4) | (shr[22] << 2) | shr[23]),
|
||||
print '$%02X, ' % ((shr[24] << 6) | (shr[25] << 4) | (shr[26] << 2) | shr[27]),
|
||||
print '$%02X' % ((shr[28] << 6) | (shr[29] << 4) | (shr[30] << 2) | shr[31])
|
||||
pygame.display.flip()
|
Loading…
Reference in New Issue
Block a user