Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- '''
- ----------------------------------------------------------------------------
- RGB Image to MSX Converter
- Original Graphic routine To convert RGB images To MSX1 video,
- made in BlitzBasic by Leandro Correia (www.leandrocorreia.com)
- python 2.7 converted by MsxKun (uses PIL library)
- ----------------------------------------------------------------------------
- '''
- #from os.path import basename, splitext, dirname, abspath
- import sys
- import math
- from PIL import Image, ImageDraw
- '''
- ----------------------------------------------------------------------------
- Calculate Distance Function
- ----------------------------------------------------------------------------
- Convert two RGB color values into Lab and uses the CIEDE2000 formula to calculate
- the distance between them.
- This function first converts RGBs to XYZ and then to Lab.
- This is not optimized, but I did my best to make it readable.
- In some rare cases there are some weird colors, so MAYBE there's a small bug in
- the implementation.
- Or it could be improved since RGB To Lab is Not a direct conversion.
- Note: As we're using PIL, maybe we can do this (need to check):
- The following example converts an RGB image (linearly calibrated according to
- ITU-R 709, using the D65 luminant) to the CIE XYZ color space:
- rgb2xyz = (
- 0.412453, 0.357580, 0.180423, 0,
- 0.212671, 0.715160, 0.072169, 0,
- 0.019334, 0.119193, 0.950227, 0 )
- out = im.convert("RGB", rgb2xyz)
- '''
- def calcdist2000(r1, g1, b1, r2, g2, b2):
- ''' Converting RGB values into XYZ - color 1'''
- r = r1/255.0
- g = g1/255.0
- b = b1/255.0
- r = ((r+0.055)/1.055)**2.4 if (r > 0.04045) else r/12.92
- g = ((g+0.055)/1.055)**2.4 if (g > 0.04045) else g/12.92
- b = ((b+0.055)/1.055)**2.4 if (b > 0.04045) else b/12.92
- r = r*100.0
- g = g*100.0
- b = b*100.0
- x = r*0.4124 + g*0.3576 + b*0.1805
- y = r*0.2126 + g*0.7152 + b*0.0722
- z = r*0.0193 + g*0.1192 + b*0.9505
- x = x/95.047
- y = y/100.000
- z = z/108.883
- x = x**(1.0/3.0) if (x > 0.008856) else (7.787*x) + (16.0/116.0)
- y = y**(1.0/3.0) if (y > 0.008856) else (7.787*y) + (16.0/116.0)
- z = z**(1.0/3.0) if (z > 0.008856) else (7.787*z) + (16.0/116.0)
- ''' Converts XYZ to Lab... '''
- l1 = (116*y)-16.0
- a1 = 500.0*(x-y)
- b1 = 200.0*(y-z)
- ''' Converting RGB values into XYZ - color 2'''
- r = r2/255.0
- g = g2/255.0
- b = b2/255.0
- r = ((r+0.055)/1.055)**2.4 if (r > 0.04045) else r/12.92
- g = ((g+0.055)/1.055)**2.4 if (g > 0.04045) else g/12.92
- b = ((b+0.055)/1.055)**2.4 if (b > 0.04045) else b/12.92
- r = r*100.0
- g = g*100.0
- b = b*100.0
- x = r*0.4124 + g*0.3576 + b*0.1805
- y = r*0.2126 + g*0.7152 + b*0.0722
- z = r*0.0193 + g*0.1192 + b*0.9505
- x = x/95.047
- y = y/100.000
- z = z/108.883
- x = x**(1.0/3.0) if (x > 0.008856) else (7.787*x) + (16.0/116.0)
- y = y**(1.0/3.0) if (y > 0.008856) else (7.787*y) + (16.0/116.0)
- z = z**(1.0/3.0) if (z > 0.008856) else (7.787*z) + (16.0/116.0)
- ''' Converts XYZ to Lab... '''
- l2 = (116*y)-16.0
- a2 = 500.0*(x-y)
- b2 = 200.0*(y-z)
- ''' ...and then calculates distance between Lab colors, using the CIEDE2000 formula. '''
- dl = l2-l1
- hl = l1+dl*0.5
- sqb1 = b1*b1
- sqb2 = b2*b2
- c1 = math.sqrt(a1*a1+sqb1)
- c2 = math.sqrt(a2*a2+sqb2)
- hc7 = ((c1+c2)*0.5)**7
- trc = math.sqrt(hc7/(hc7+6103515625.0))
- t2 = 1.5-trc*0.5
- ap1 = a1*t2
- ap2 = a2*t2
- c1 = math.sqrt(ap1*ap1+sqb1)
- c2 = math.sqrt(ap2*ap2+sqb2)
- dc = c2-c1
- hc = c1+dc*0.5
- hc7 = hc**7.0
- trc = math.sqrt(hc7/(hc7+6103515625.0))
- h1 = math.atan2(b1,ap1)
- if (h1 < 0):
- h1 = h1+math.pi*2.0
- h2 = math.atan2(b2,ap2)
- if (h2 < 0):
- h2 = h2+math.pi*2.0
- hdiff = h2-h1
- hh = h1+h2
- if (math.fabs(hdiff) > math.pi):
- hh = hh + math.pi*2
- if (h2 <= h1):
- hdiff = hdiff + math.pi*2.0
- else:
- hdiff = hdiff-math.pi*2.0
- hh = hh*0.5
- t2 = 1-0.17*math.cos(hh-math.pi/6)+0.24*math.cos(hh*2)
- t2 = t2+0.32*math.cos(hh*3+math.pi/30.0)
- t2 = t2-0.2*math.cos(hh*4-math.pi*63/180.0)
- dh = 2*math.sqrt(c1*c2)*math.sin(hdiff*0.5)
- sqhl = (hl-50)*(hl-50)
- fl = dl/(1+(0.015*sqhl/squaretable[20+int(sqhl)])) # here sqr lookup table is used!
- #fl = dl/(1+(0.015*sqhl/math.sqrt(20.0+sqhl))) # here not using sqr lookup table
- fc = dc/(hc*0.045+1.0)
- fh = dh/(t2*hc*0.015+1.0)
- dt = 30*math.exp(-(36.0*hh-55.0*math.pi**2.0)/(25.0*math.pi*math.pi))
- r = 0-2*trc*math.sin(2.0*dt*math.pi/180.0)
- return math.sqrt(fl*fl+fc*fc+fh*fh+r*fc*fh)
- '''
- ----------------------------------------------------------------------------
- Main Code
- ----------------------------------------------------------------------------
- '''
- ''' Image to be loaded and converted '''
- #inputfile=str(sys.argv[1])
- inputfile = "gameover.png"
- outputfile = "gameover.sc2"
- previewfile = "gameover_converted.bmp"
- input_im = Image.open(inputfile) # Can be many different formats.
- pix = input_im.load()
- input_im.show()
- output_im = Image.new('RGB', (256,192), color = 'white')
- draw = ImageDraw.Draw(output_im)
- '''
- Color tolerance for dithering (from 0 to 100).
- Higher values mean dithering between colors that are not similar,
- which results in better color accuracy but ugly squares on degradees.
- 0 means no dithering
- '''
- tolerance = 100
- ''' Data of the MSX palette RGB values '''
- palette = [0,0,0,0,0,0,36,219,36,109,255,109,36,36,255,73,109,255,182,36,36,73,219,
- 255,255,36,36,255,109,109,219,219,36,219,219,146,36,146,36,219,73,182,182,
- 182,182,255,255,255]
- msxr = palette[0::3]
- msxg = palette[1::3]
- msxb = palette[2::3]
- color = [0,0,0]
- octetr = [0] *8
- octetg = [0] *8
- octetb = [0] *8
- octetfinal = [0] *8
- octetvalue = [0] *8
- toner = [0] *5
- toneg = [0] *5
- toneb = [0] *5
- distcolor = [0] *5
- ''' Lookup table to make squareroots quicker... '''
- squaretable = [0] *9999999
- for i in range(9999999):
- squaretable[i]=math.sqrt(i)
- ''' Loop '''
- imgh = 192
- imgw = 256
- y = 0
- x = 0
- while y < 192:
- bestdistance = 99999999
- ''' Get the RGB values of 8 pixels of the original image '''
- for i in range(8):
- rgba = pix[x+i,y] # Get the RGBA Value of the a pixel of an image
- octetr[i] = rgba[0]
- octetg[i] = rgba[1]
- octetb[i] = rgba[2]
- ''' Brute force starts. Program tests all 15 x 15 MSX color combinations.
- For each pixel octet it'll have to compare the original pixel colors with
- three diffent colors: two MSX colors and a mixed RGB of both. '''
- cor1 = 0
- cor2 = 0
- for cor1 in range(1,16):
- for cor2 in range(cor1,16):
- dist = 0
- ''' First MSX color of the octet '''
- toner[0] = msxr [cor1]
- toneg[0] = msxg [cor1]
- toneb[0] = msxb [cor1]
- ''' Second MSX color of the octet '''
- toner[2] = msxr [cor2]
- toneg[2] = msxg [cor2]
- toneb[2] = msxb [cor2]
- ''' A mix of both MSX colors RGB values. Since MSX cannot mix colors, later
- if this color is chosen it'll be substituted by a 2x2 dithering pattern. '''
- toner[1] = (msxr[cor1] + msxr[cor2]) / 2
- toneg[1] = (msxg[cor1] + msxg[cor2]) / 2
- toneb[1] = (msxb[cor1] + msxb[cor2]) / 2
- cd = calcdist2000(msxr[cor1],msxg[cor1],msxb[cor1],msxr[cor2],msxg[cor2],msxb[cor2])
- ''' If colors are not too distant, octect can be either dithered or not '''
- if (cd <= tolerance):
- ''' dithered '''
- for i in range(8):
- for j in range(3):
- distcolor[j] = calcdist2000(toner[j],toneg[j],toneb[j],octetr[i],octetg[i],octetb[i])
- finaldist = distcolor[0]
- octetvalue[i] = 0
- for j in range(3):
- if (distcolor[j] < finaldist):
- finaldist = distcolor[j]
- octetvalue[i] = j
- dist = dist + finaldist
- if (dist > bestdistance):
- break
- else:
- ''' not dithered '''
- for i in range(8):
- finaldista = calcdist2000(toner[0],toneg[0],toneb[0],octetr[i],octetg[i],octetb[i])
- finaldistb = calcdist2000(toner[2],toneg[2],toneb[2],octetr[i],octetg[i],octetb[i])
- if (finaldista < finaldistb):
- octetvalue[i] = 0
- finaldist = finaldista
- else:
- octetvalue[i] = 2
- finaldist = finaldistb
- dist = dist + finaldist
- if (dist > bestdistance):
- break
- if (dist < bestdistance):
- bestdistance = dist
- bestcor1 = cor1
- bestcor2 = cor2
- for i in range (8):
- octetfinal[i] = octetvalue[i]
- if (bestdistance == 0):
- break
- if (bestdistance == 0):
- break
- byte = 0
- for i in range(8):
- if (octetfinal[i] == 0):
- color = [msxr[bestcor1],msxg[bestcor1],msxb[bestcor1]]
- elif (octetfinal[i] == 1):
- if ((y % 2) == (i % 2)):
- color = [msxr[bestcor2],msxg[bestcor2],msxb[bestcor2]]
- byte = byte + 2 ** (7-i)
- else:
- color = [msxr[bestcor1],msxg[bestcor1],msxb[bestcor1]]
- elif (octetfinal[i] == 2):
- color = [msxr[bestcor2],msxg[bestcor2],msxb[bestcor2]]
- byte = byte + 2 ** (7 - i)
- draw.point((x + i, y), fill=(color[0],color[1],color[2]))
- y = y + 1
- if ((y % 8) == 0):
- y = y - 8
- x = x + 8
- if (x > 255):
- x = 0
- y = y + 8
- # This would be the place for you to write the bytes in the final MSX screen file.
- #output.append(byte) # ???
- ''' Shows output image '''
- output_im.show()
- output_im.save(previewfile,"BMP")
- ''' Save File SC2 '''
- # To Do
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement