Dieses Projekt dient der digitalen Umwandlung von Bildern in simulierte Darstellung aus Sicht eines rot-grün-blinden Menschen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Dyschromasie-Applikation.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. from PIL import Image, ImageTk
  2. import PIL
  3. import tkinter as tk
  4. from tkinter import filedialog, messagebox
  5. import cv2
  6. import numpy as np
  7. root = tk.Tk()
  8. simGrad = tk.IntVar(root)
  9. class Dyschromasie:
  10. cb_image = np.array([]).astype('float64')
  11. sim_image = np.array([]).astype('uint8')
  12. def __init__(self, img_mat=np.array([]), rows=0, cols=0, kanaele=0,sim_faktor=0):
  13. self.rows = rows
  14. self.cols = cols
  15. self.kanaele = kanaele
  16. self.img_mat = img_mat
  17. self.sim_faktor = sim_faktor
  18. T = np.array([[0.31399022, 0.63951294, 0.04649755],
  19. [0.15537241, 0.75789446, 0.08670142],
  20. [0.01775239, 0.10944209, 0.87256922]])
  21. T_reversed = np.array([[5.47221206, -4.6419601, 0.16963708],
  22. [-1.1252419, 2.29317094, -0.1678952],
  23. [0.02980165, -0.19318073, 1.16364789]])
  24. def gammaCorrection(self, v):
  25. if v <= 0.04045 * 255:
  26. return float(((v / 255) / 12.92))
  27. elif v > 0.04045 * 255:
  28. return float((((v / 255) + 0.055) / 1.055) ** 2.4)
  29. def reverseGammaCorrection(self, v_reverse):
  30. if v_reverse <= 0.0031308:
  31. return int(255 * (12.92 * v_reverse))
  32. elif v_reverse > 0.0031308:
  33. return int(255 * (1.055 * v_reverse ** 0.41666 - 0.055))
  34. class Protanopie(Dyschromasie):
  35. def Simulate(self):
  36. sim_mat = np.array([[(1 - self.sim_faktor), 1.05118294 * self.sim_faktor, -0.05116099 * self.sim_faktor],
  37. [0, 1, 0],
  38. [0, 0, 1]])
  39. # Gammakorrektur durchfuehren
  40. self.cb_image = np.copy(self.img_mat).astype('float64')
  41. for i in range(self.rows):
  42. for j in range(self.cols):
  43. for x in range(3):
  44. self.cb_image[i, j, x] = self.gammaCorrection(self.img_mat[i, j, x])
  45. # Einzelne Pixelwertberechnung
  46. for i in range(self.rows):
  47. for j in range(self.cols):
  48. self.cb_image[i, j] = self.T_reversed.dot(sim_mat).dot(self.T).dot(self.cb_image[i, j])
  49. self.sim_image = np.copy(self.cb_image)
  50. self.sim_image = self.sim_image.astype('uint8')
  51. # Rücktransformation der Gammawerte
  52. for i in range(self.rows):
  53. for j in range(self.cols):
  54. for x in range(3):
  55. self.sim_image[i, j, x] = self.reverseGammaCorrection(self.cb_image[i, j, x])
  56. return self.sim_image
  57. class Deuteranopie(Dyschromasie):
  58. def Simulate(self):
  59. sim_mat = np.array([[1, 0, 0],
  60. [0.9513092 * self.sim_faktor, (1 - self.sim_faktor), 0.04866992 * self.sim_faktor],
  61. [0, 0, 1]])
  62. # Gammakorrektur durchfuehren
  63. self.cb_image = np.copy(self.img_mat).astype('float64')
  64. for i in range(self.rows):
  65. for j in range(self.cols):
  66. for x in range(3):
  67. self.cb_image[i, j, x] = self.gammaCorrection(self.img_mat[i, j, x])
  68. # Einzelne Pixelwertberechnung
  69. for i in range(self.rows):
  70. for j in range(self.cols):
  71. self.cb_image[i, j] = self.T_reversed.dot(sim_mat).dot(self.T).dot(self.cb_image[i, j])
  72. self.sim_image = np.copy(self.cb_image)
  73. self.sim_image = self.sim_image.astype('uint8')
  74. # Rücktransformation der Gammawerte
  75. for i in range(self.rows):
  76. for j in range(self.cols):
  77. for x in range(3):
  78. self.sim_image[i, j, x] = self.reverseGammaCorrection(self.cb_image[i, j, x])
  79. return self.sim_image
  80. class Tritanopie(Dyschromasie):
  81. def Simulate(self):
  82. sim_mat = np.array([[1, 0, 0],
  83. [0, 1, 0],
  84. [-0.86744736 * self.sim_faktor, 1.86727089 * self.sim_faktor, (1 - self.sim_faktor)]])
  85. # Gammakorrektur durchfuehren
  86. self.cb_image = np.copy(self.img_mat).astype('float64')
  87. for i in range(self.rows):
  88. for j in range(self.cols):
  89. for x in range(3):
  90. self.cb_image[i, j, x] = self.gammaCorrection(self.img_mat[i, j, x])
  91. # Einzelne Pixelwertberechnung
  92. for i in range(self.rows):
  93. for j in range(self.cols):
  94. self.cb_image[i, j] = self.T_reversed.dot(sim_mat).dot(self.T).dot(self.cb_image[i, j])
  95. self.sim_image = np.copy(self.cb_image)
  96. self.sim_image = self.sim_image.astype('uint8')
  97. # Rücktransformation der Gammawerte
  98. for i in range(self.rows):
  99. for j in range(self.cols):
  100. for x in range(3):
  101. self.sim_image[i, j, x] = self.reverseGammaCorrection(self.cb_image[i, j, x])
  102. return self.sim_image
  103. class AutoScrollbar(tk.Scrollbar):
  104. # A scrollbar that hides itself if it's not needed.
  105. # Only works if you use the grid geometry manager!
  106. def set(self, lo, hi):
  107. if float(lo) <= 0.0 and float(hi) >= 1.0:
  108. # grid_remove is currently missing from Tkinter!
  109. self.tk.call("grid", "remove", self)
  110. else:
  111. self.grid()
  112. tk.Scrollbar.set(self, lo, hi)
  113. def pack(self, **kw):
  114. raise TclError("cannot use pack with this widget")
  115. def place(self, **kw):
  116. raise TclError("cannot use place with this widget")
  117. class ScrollFrame:
  118. def __init__(self, master):
  119. self.vscrollbar = AutoScrollbar(master)
  120. self.vscrollbar.grid(row=0, column=1, sticky='ns')
  121. self.hscrollbar = AutoScrollbar(master, orient='horizontal')
  122. self.hscrollbar.grid(row=1, column=0, sticky='ew')
  123. self.canvas = tk.Canvas(master, yscrollcommand=self.vscrollbar.set,
  124. xscrollcommand=self.hscrollbar.set)
  125. self.canvas.grid(row=0, column=0, sticky='nsew')
  126. self.vscrollbar.config(command=self.canvas.yview)
  127. self.hscrollbar.config(command=self.canvas.xview)
  128. # make the canvas expandable
  129. master.grid_rowconfigure(0, weight=1)
  130. master.grid_columnconfigure(0, weight=1)
  131. # create frame inside canvas
  132. self.frame = tk.Frame(self.canvas)
  133. self.frame.rowconfigure(1, weight=1)
  134. self.frame.columnconfigure(1, weight=1)
  135. # update the frame
  136. self.frame.bind("<Configure>", self.reset_scrollregion)
  137. def reset_scrollregion(self, event):
  138. self.canvas.configure(scrollregion=self.canvas.bbox('all'))
  139. def update(self):
  140. self.canvas.create_window(0, 0, anchor='nw', window=self.frame)
  141. self.frame.update_idletasks()
  142. self.canvas.config(scrollregion=self.canvas.bbox("all"))
  143. if self.frame.winfo_reqwidth() != self.canvas.winfo_width():
  144. # update the canvas's width to fit the inner frame
  145. self.canvas.config(width = self.frame.winfo_reqwidth())
  146. if self.frame.winfo_reqheight() != self.canvas.winfo_height():
  147. # update the canvas's width to fit the inner frame
  148. self.canvas.config(height = self.frame.winfo_reqheight())
  149. root.title("Projekt Dyschromasie")
  150. SB = ScrollFrame(root)
  151. img = np.array([])
  152. rows = 0
  153. cols = 0
  154. kanaele = 0
  155. sim_pro = tk.IntVar(root)
  156. sim_deut = tk.IntVar(root)
  157. sim_tri = tk.IntVar(root)
  158. simulationsGradient = tk.Scale(SB.frame, from_=0, to_=100, variable=simGrad, orient='horizontal')
  159. simulationsGradient.grid(column= 0, row = 1, columnspan=10)
  160. def browse():
  161. # Auswahl des FilePaths
  162. try:
  163. path = tk.filedialog.askopenfilename(filetypes=[("Image File", '.jpg')])
  164. im = Image.open(path)
  165. except:
  166. tk.messagebox.showerror(title='Datenfehler', message='Kein Bild gefunden/ausgewählt')
  167. global simulateButton
  168. if len(path) > 0:
  169. simulateButton.config(state='active')
  170. # Anzeigen des Bildes
  171. tkimage = ImageTk.PhotoImage(im)
  172. myvar = tk.Label(SB.frame, image=tkimage)
  173. myvar.image = tkimage
  174. myvar.grid(columnspan=5)
  175. # Einspeichern der Path-Informationen
  176. global img, rows, cols, kanaele
  177. img = cv2.imread(path)
  178. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  179. rows, cols, kanaele = img.shape
  180. def simulate():
  181. global img, rows, cols, kanaele, sim_pro, sim_deut, sim_tri
  182. if sim_deut.get():
  183. d = Deuteranopie(img, rows, cols, kanaele, simGrad.get()/100)
  184. display_array_deut = d.Simulate()
  185. T = tk.Text(SB.frame, height=1, width=15)
  186. T.grid(columnspan=5)
  187. print_string = "Deutranop(" + str(round(d.sim_faktor*100)) + "%)"
  188. T.insert('current', print_string)
  189. conv_SimulationPic_deut = ImageTk.PhotoImage(image=PIL.Image.fromarray(display_array_deut))
  190. sim_pic_deut = tk.Label(SB.frame, image=conv_SimulationPic_deut)
  191. sim_pic_deut.Image = conv_SimulationPic_deut
  192. sim_pic_deut.grid(columnspan=5)
  193. elif sim_tri.get():
  194. t = Tritanopie(img, rows, cols, kanaele, simGrad.get()/100)
  195. display_array_tri = t.Simulate()
  196. T = tk.Text(SB.frame, height=1, width=15)
  197. T.grid(columnspan=5)
  198. print_string = "Tritanop(" + str(round(t.sim_faktor * 100)) + "%)"
  199. T.insert('current', print_string)
  200. conv_SimulationPic_tri = ImageTk.PhotoImage(image=PIL.Image.fromarray(display_array_tri))
  201. sim_pic_tri = tk.Label(SB.frame, image=conv_SimulationPic_tri)
  202. sim_pic_tri.Image = conv_SimulationPic_tri
  203. sim_pic_tri.grid(columnspan=5)
  204. elif sim_pro.get():
  205. p = Protanopie(img, rows, cols, kanaele, simGrad.get()/100)
  206. display_array_pro = p.Simulate()
  207. T = tk.Text(SB.frame, height=1, width=15)
  208. T.grid(columnspan=5)
  209. print_string = "Protanop(" + str(round(p.sim_faktor * 100)) + "%)"
  210. T.insert('current', print_string)
  211. conv_SimulationPic_pro = ImageTk.PhotoImage(image=PIL.Image.fromarray(display_array_pro))
  212. sim_pic_pro = tk.Label(SB.frame, image=conv_SimulationPic_pro)
  213. sim_pic_pro.Image = conv_SimulationPic_pro
  214. sim_pic_pro.grid(columnspan=5)
  215. btn = tk.Button(SB.frame, text="Browse", width=25, command=browse, bg='light blue')
  216. btn.grid(column=0, row=0, columnspan=2)
  217. simulateButton = tk.Button(SB.frame, text="Simulate", width=25, command=simulate, bg='light blue')
  218. simulateButton.grid(column=1, row=0, columnspan=2)
  219. simulateButton.config(state='disabled')
  220. checkButton_p = tk.Checkbutton(SB.frame, text="Protanop", variable=sim_pro, onvalue=1, offvalue=0, height=5, width=20)
  221. checkButton_d = tk.Checkbutton(SB.frame, text="Deutanop", variable=sim_deut, onvalue=1, offvalue=0, height=5, width=20)
  222. checkButton_t = tk.Checkbutton(SB.frame, text="Tritanop", variable=sim_tri, onvalue=1, offvalue=0, height=5, width=20)
  223. checkButton_p.grid(column=0, row=2)
  224. checkButton_d.grid(column=1, row=2)
  225. checkButton_t.grid(column=2, row=2)
  226. SB.update()
  227. root.mainloop()