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

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