• API
• FAQ
• Tools
• Archive
SHARE
TWEET

# RGB2MSX_python_v2

a guest Apr 3rd, 2019 39 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. '''
2. ----------------------------------------------------------------------------
3. RGB Image to MSX Converter
4.
5. Original Graphic routine To convert RGB images To MSX1 video,
6. made in BlitzBasic by Leandro Correia (www.leandrocorreia.com)
7.
8. python 2.7 converted by MsxKun (uses PIL library)
9. ----------------------------------------------------------------------------
10. '''
11.
12. #from os.path import basename, splitext, dirname, abspath
13.
14. import sys
15. import math
16.
17. from PIL import Image, ImageDraw
18.
19. '''
20. ----------------------------------------------------------------------------
21. Calculate Distance Function
22. ----------------------------------------------------------------------------
23.
24. Convert two RGB color values into Lab and uses the CIEDE2000 formula to calculate
25. the distance between them.
26. This function first converts RGBs to XYZ and then to Lab.
27.
28. This is not optimized, but I did my best to make it readable.
29. In some rare cases there are some weird colors, so MAYBE there's a small bug in
30. the implementation.
31. Or it could be improved since RGB To Lab is Not a direct conversion.
32.
33. Note: As we're using PIL, maybe we can do this (need to check):
34.
35. The following example converts an RGB image (linearly calibrated according to
36. ITU-R 709, using the D65 luminant) to the CIE XYZ color space:
37.
38. rgb2xyz = (
39.    0.412453, 0.357580, 0.180423, 0,
40.    0.212671, 0.715160, 0.072169, 0,
41.    0.019334, 0.119193, 0.950227, 0 )
42. out = im.convert("RGB", rgb2xyz)
43.
44. '''
45.
46. def calcdist2000(r1, g1, b1, r2, g2, b2):
47.
48.     ''' Converting RGB values into XYZ - color 1'''
49.
50.     r = r1/255.0
51.     g = g1/255.0
52.     b = b1/255.0
53.
54.     r = ((r+0.055)/1.055)**2.4 if (r > 0.04045) else r/12.92
55.     g = ((g+0.055)/1.055)**2.4 if (g > 0.04045) else g/12.92
56.     b = ((b+0.055)/1.055)**2.4 if (b > 0.04045) else b/12.92
57.
58.     r = r*100.0
59.     g = g*100.0
60.     b = b*100.0
61.
62.     x = r*0.4124 + g*0.3576 + b*0.1805
63.     y = r*0.2126 + g*0.7152 + b*0.0722
64.     z = r*0.0193 + g*0.1192 + b*0.9505
65.
66.     x = x/95.047
67.     y = y/100.000
68.     z = z/108.883
69.
70.     x = x**(1.0/3.0) if (x > 0.008856) else (7.787*x) + (16.0/116.0)
71.     y = y**(1.0/3.0) if (y > 0.008856) else (7.787*y) + (16.0/116.0)
72.     z = z**(1.0/3.0) if (z > 0.008856) else (7.787*z) + (16.0/116.0)
73.
74.     ''' Converts XYZ to Lab... '''
75.
76.     l1 = (116*y)-16.0
77.     a1 = 500.0*(x-y)
78.     b1 = 200.0*(y-z)
79.
80.     ''' Converting RGB values into XYZ - color 2'''
81.
82.     r = r2/255.0
83.     g = g2/255.0
84.     b = b2/255.0
85.
86.     r = ((r+0.055)/1.055)**2.4 if (r > 0.04045) else r/12.92
87.     g = ((g+0.055)/1.055)**2.4 if (g > 0.04045) else g/12.92
88.     b = ((b+0.055)/1.055)**2.4 if (b > 0.04045) else b/12.92
89.
90.     r = r*100.0
91.     g = g*100.0
92.     b = b*100.0
93.
94.     x = r*0.4124 + g*0.3576 + b*0.1805
95.     y = r*0.2126 + g*0.7152 + b*0.0722
96.     z = r*0.0193 + g*0.1192 + b*0.9505
97.
98.     x = x/95.047
99.     y = y/100.000
100.     z = z/108.883
101.
102.     x = x**(1.0/3.0) if (x > 0.008856) else (7.787*x) + (16.0/116.0)
103.     y = y**(1.0/3.0) if (y > 0.008856) else (7.787*y) + (16.0/116.0)
104.     z = z**(1.0/3.0) if (z > 0.008856) else (7.787*z) + (16.0/116.0)
105.
106.     ''' Converts XYZ to Lab... '''
107.
108.     l2 = (116*y)-16.0
109.     a2 = 500.0*(x-y)
110.     b2 = 200.0*(y-z)
111.
112.     ''' ...and then calculates distance between Lab colors, using the CIEDE2000 formula. '''
113.
114.     dl = l2-l1
115.     hl = l1+dl*0.5
116.     sqb1 = b1*b1
117.     sqb2 = b2*b2
118.     c1 = math.sqrt(a1*a1+sqb1)
119.     c2 = math.sqrt(a2*a2+sqb2)
120.     hc7 = ((c1+c2)*0.5)**7
121.     trc = math.sqrt(hc7/(hc7+6103515625.0))
122.     t2 = 1.5-trc*0.5
123.     ap1 = a1*t2
124.     ap2 = a2*t2
125.     c1 = math.sqrt(ap1*ap1+sqb1)
126.     c2 = math.sqrt(ap2*ap2+sqb2)
127.     dc = c2-c1
128.     hc = c1+dc*0.5
129.     hc7 = hc**7.0
130.     trc = math.sqrt(hc7/(hc7+6103515625.0))
131.     h1 = math.atan2(b1,ap1)
132.     if (h1 < 0):
133.         h1 = h1+math.pi*2.0
134.     h2 = math.atan2(b2,ap2)
135.     if (h2 < 0):
136.          h2 = h2+math.pi*2.0
137.     hdiff = h2-h1
138.     hh = h1+h2
139.
140.     if (math.fabs(hdiff) > math.pi):
141.         hh = hh + math.pi*2
142.         if (h2 <= h1):
143.             hdiff = hdiff + math.pi*2.0
144.     else:
145.         hdiff = hdiff-math.pi*2.0
146.
147.     hh = hh*0.5
148.     t2 = 1-0.17*math.cos(hh-math.pi/6)+0.24*math.cos(hh*2)
149.     t2 = t2+0.32*math.cos(hh*3+math.pi/30.0)
150.     t2 = t2-0.2*math.cos(hh*4-math.pi*63/180.0)
151.     dh = 2*math.sqrt(c1*c2)*math.sin(hdiff*0.5)
152.     sqhl = (hl-50)*(hl-50)
153.     fl = dl/(1+(0.015*sqhl/squaretable[20+int(sqhl)])) # here sqr lookup table is used!
154.     #fl = dl/(1+(0.015*sqhl/math.sqrt(20.0+sqhl))) # here not using sqr lookup table
155.     fc = dc/(hc*0.045+1.0)
156.     fh = dh/(t2*hc*0.015+1.0)
157.     dt = 30*math.exp(-(36.0*hh-55.0*math.pi**2.0)/(25.0*math.pi*math.pi))
158.     r = 0-2*trc*math.sin(2.0*dt*math.pi/180.0)
159.
160.     return math.sqrt(fl*fl+fc*fc+fh*fh+r*fc*fh)
161.
162. '''
163. ----------------------------------------------------------------------------
164. Main Code
165. ----------------------------------------------------------------------------
166. '''
167.
168. ''' Image to be loaded and converted '''
169.
170. #inputfile=str(sys.argv[1])
171.
172. inputfile = "test2.png"
173. outputfile = "test2.sc2"
174. patfile = "test2.pat"
175. colfile = "test2.col"
176. previewfile = "test2_conv.bmp"
177.
178. patbuffer = bytearray()
179. colbuffer = bytearray()
180.
181. input_im = Image.open(inputfile)            # Can be many different formats.
183. #input_im.show()
184.
185. output_im = Image.new('RGB', (256,192), color = 'white')
186. draw = ImageDraw.Draw(output_im)
187.
188. '''
189. Color tolerance for dithering (from 0 to 100).
190. Higher values mean dithering between colors that are not similar,
191. which results in better color accuracy but ugly squares on degradees.
192. 0 means no dithering
193. '''
194.
195. tolerance = 100
196.
197. ''' Data of the MSX palette RGB values '''
198.
199. palette = [0,0,0,0,0,0,36,219,36,109,255,109,36,36,255,73,109,255,182,36,36,73,219,
200.             255,255,36,36,255,109,109,219,219,36,219,219,146,36,146,36,219,73,182,182,
201.             182,182,255,255,255]
202.
203. msxr = palette[0::3]
204. msxg = palette[1::3]
205. msxb = palette[2::3]
206.
207. color = [0,0,0]
208.
209. octetr = [0] *8
210. octetg = [0] *8
211. octetb = [0] *8
212.
213. octetfinal = [0] *8
214. octetvalue = [0] *8
215.
216. toner = [0] *5
217. toneg = [0] *5
218. toneb = [0] *5
219. distcolor = [0] *5
220.
221. ''' Lookup table to make squareroots quicker... '''
222.
223. squaretable = [0] *9999999
224.
225. for i in range(9999999):
226.     squaretable[i]=math.sqrt(i)
227.
228. ''' Loop '''
229.
230. imgh = 192
231. imgw = 256
232. y = 0
233. x = 0
234.
235. while y < 192:
236.
237.     bestdistance = 99999999
238.
239.     ''' Get the RGB values of 8 pixels of the original image '''
240.
241.     for i in range(8)
242.
243.         rgba = pix[x+i,y]       # Get the RGBA Value of the a pixel of an image
244.         octetr[i] = rgba[0]
245.         octetg[i] = rgba[1]
246.         octetb[i] = rgba[2]
247.
248.     ''' Brute force starts. Program tests all 15 x 15 MSX color combinations.
249.         For each pixel octet it'll have to compare the original pixel colors with
250.         three diffent colors: two MSX colors and a mixed RGB of both. '''
251.
252.     cor1 = 0
253.     cor2 = 0
254.
255.     for cor1 in range(1,16):
256.         for cor2 in range(cor1,16):
257.
258.             dist = 0
259.
260.             ''' First MSX color of the octet '''
261.
262.             toner[0] = msxr [cor1]
263.             toneg[0] = msxg [cor1]
264.             toneb[0] = msxb [cor1]
265.
266.             ''' Second MSX color of the octet '''
267.
268.             toner[2] = msxr [cor2]
269.             toneg[2] = msxg [cor2]
270.             toneb[2] = msxb [cor2]
271.
272.             ''' A mix of both MSX colors RGB values. Since MSX cannot mix colors, later
273.                 if this color is chosen it'll be substituted by a 2x2 dithering pattern. '''
274.
275.             toner[1] = (msxr[cor1] + msxr[cor2]) / 2
276.             toneg[1] = (msxg[cor1] + msxg[cor2]) / 2
277.             toneb[1] = (msxb[cor1] + msxb[cor2]) / 2
278.
279.             cd = calcdist2000(msxr[cor1],msxg[cor1],msxb[cor1],msxr[cor2],msxg[cor2],msxb[cor2])
280.
281.             ''' If colors are not too distant, octect can be either dithered or not '''
282.
283.             if (cd <= tolerance):
284.
285.                 ''' dithered '''
286.
287.                 for i in range(8):
288.
289.                     for j in range(3):
290.                         distcolor[j] = calcdist2000(toner[j],toneg[j],toneb[j],octetr[i],octetg[i],octetb[i])
291.
292.                     finaldist = distcolor[0]
293.                     octetvalue[i] = 0
294.
295.                     for j in range(3):
296.                         if (distcolor[j] < finaldist):
297.                             finaldist = distcolor[j]
298.                             octetvalue[i] = j
299.
300.                     dist = dist + finaldist
301.
302.                     if (dist > bestdistance):
303.                         break
304.
305.             else:
306.
307.                 ''' not dithered '''
308.
309.                 for i in range(8):
310.
311.                     finaldista = calcdist2000(toner[0],toneg[0],toneb[0],octetr[i],octetg[i],octetb[i])
312.                     finaldistb = calcdist2000(toner[2],toneg[2],toneb[2],octetr[i],octetg[i],octetb[i])
313.
314.                     if (finaldista < finaldistb):
315.                         octetvalue[i] = 0
316.                         finaldist = finaldista
317.                     else:
318.                         octetvalue[i] = 2
319.                         finaldist = finaldistb
320.
321.                     dist = dist + finaldist
322.
323.                     if (dist > bestdistance):
324.                         break
325.
326.             if (dist < bestdistance):
327.
328.                 bestdistance = dist
329.                 bestcor1 = cor1
330.                 bestcor2 = cor2
331.                 for i in range (8):
332.                     octetfinal[i] = octetvalue[i]
333.
334.             if (bestdistance == 0):
335.                 break
336.
337.         if (bestdistance == 0):
338.                 break
339.
340.     patbyte = 0
341.     colbyte = 0
342.
343.     for i in range(8):
344.
345.         if (octetfinal[i] == 0):
346.             color = [msxr[bestcor1],msxg[bestcor1],msxb[bestcor1]]
347.
348.         elif (octetfinal[i] == 1):
349.             if ((y % 2) == (i % 2)):
350.                 color = [msxr[bestcor2],msxg[bestcor2],msxb[bestcor2]]
351.                 patbyte = patbyte + 2 ** (7-i)
352.
353.             else:
354.                 color = [msxr[bestcor1],msxg[bestcor1],msxb[bestcor1]]
355.
356.         elif (octetfinal[i] == 2):
357.             color = [msxr[bestcor2],msxg[bestcor2],msxb[bestcor2]]
358.             patbyte = patbyte + 2 ** (7 - i)
359.
360.         draw.point((x + i, y), fill=(color[0],color[1],color[2]))
361.         colbyte = bestcor2 * 16 + bestcor1
362.
363.     y = y + 1
364.
365.     if ((y % 8) == 0):
366.
367.         y = y - 8
368.         x = x + 8
369.
370.     if (x > 255):
371.         x = 0
372.         y = y + 8
373.
374.     # This would be the place for you to write the bytes in the final MSX screen file.
375.
376.     patbuffer.append(patbyte)
377.     colbuffer.append(colbyte)
378.
379. ''' Shows output image '''
380.
381. output_im.show()
382. output_im.save(previewfile,"BMP")
383.
384. ''' Save File SC2 '''
385.
387.
388. nambuf = bytearray()
389. for i in range(256):
390.     nambuf.append(i)
391. sprbuf = bytearray(1280)
392.
393. f = open(outputfile,"wb")