Compare commits

..
2 Commits
Author SHA1 Message Date
SponselMa71420 a7eab231ef Bild, welches im technical Paper verwendet wurde
Zum Ausführlichen Testen der Algorithmen.
2020-06-27 08:53:23 +00:00
SponselMa71420 3738084980 Bild, welches im technical Paper verwendet wurde
Zum Ausführlichen Testen der Algorithmen.
2020-06-27 08:53:22 +00:00
24 changed files with 14 additions and 457 deletions

No files matched your search

-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.8 (2)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
-6
View File
@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (2)" project-jdk-type="Python SDK" />
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Projekt_Dyschromasie.iml" filepath="$PROJECT_DIR$/.idea/Projekt_Dyschromasie.iml" />
</modules>
</component>
</project>
Generated
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
-105
View File
@@ -1,105 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="7500fa3a-b013-4e17-984f-eefa85ecceed" name="Default Changelist" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectId" id="1e30iP9BsZRaeOrrN9JBEG6XvPT" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
</component>
<component name="RunManager">
<configuration name="Dyschromasie" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="Projekt_Dyschromasie" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/Code" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/Code/Dyschromasie.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.Dyschromasie" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="7500fa3a-b013-4e17-984f-eefa85ecceed" name="Default Changelist" comment="" />
<created>1593535095255</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1593535095255</updated>
<workItem from="1593535098710" duration="1145000" />
<workItem from="1593601045161" duration="890000" />
<workItem from="1593693652245" duration="183000" />
<workItem from="1593852460532" duration="308000" />
<workItem from="1594452919558" duration="449000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="2" />
</component>
<component name="WindowStateProjectService">
<state x="739" y="275" key="FileChooserDialogImpl" timestamp="1593535189103">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="739" y="275" key="FileChooserDialogImpl/0.0.1920.1040/-1920.109.1920.1040@0.0.1920.1040" timestamp="1593535189103" />
<state width="1877" height="282" key="GridCell.Tab.0.bottom" timestamp="1594453344568">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="282" key="GridCell.Tab.0.bottom/0.0.1920.1040/-1920.109.1920.1040@0.0.1920.1040" timestamp="1594453344568" />
<state width="1877" height="282" key="GridCell.Tab.0.center" timestamp="1594453344568">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="282" key="GridCell.Tab.0.center/0.0.1920.1040/-1920.109.1920.1040@0.0.1920.1040" timestamp="1594453344568" />
<state width="1877" height="282" key="GridCell.Tab.0.left" timestamp="1594453344567">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="282" key="GridCell.Tab.0.left/0.0.1920.1040/-1920.109.1920.1040@0.0.1920.1040" timestamp="1594453344567" />
<state width="1877" height="282" key="GridCell.Tab.0.right" timestamp="1594453344568">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="282" key="GridCell.Tab.0.right/0.0.1920.1040/-1920.109.1920.1040@0.0.1920.1040" timestamp="1594453344568" />
<state x="461" y="165" key="SettingsEditor" timestamp="1593535127245">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="461" y="165" key="SettingsEditor/0.0.1920.1040/-1920.109.1920.1040@0.0.1920.1040" timestamp="1593535127245" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/Projekt_Dyschromasie$Dyschromasie.coverage" NAME="Dyschromasie Coverage Results" MODIFIED="1594453344546" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Code" />
</component>
</project>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-116
View File
@@ -1,116 +0,0 @@
from PIL import Image, ImageTk
import PIL
import tkinter as tk
import cv2
import numpy as np
from Farbaenderung import Dyschromasie
from Scrollbar import ScrollFrame
root = tk.Tk()
simGrad = tk.IntVar(root)
root.title("Projekt Dyschromasie")
SB = ScrollFrame(root)
img = np.array([])
rows = 0
cols = 0
kanaele = 0
sim_pro = tk.IntVar(root)
sim_deut = tk.IntVar(root)
sim_tri = tk.IntVar(root)
simulationsGradient = tk.Scale(SB.frame, from_=0, to_=100, variable=simGrad, orient='horizontal')
simulationsGradient.grid(column=0, row=1, columnspan=10)
def browse():
# Auswahl des FilePaths
try:
path = tk.filedialog.askopenfilename(filetypes=[("Image File", '.jpg'), ("Image File", '.png')])
im = Image.open(path)
except:
tk.messagebox.showerror(title='Datenfehler', message='Kein Bild gefunden/ausgewählt')
global simulateButton
if len(path) > 0:
simulateButton.config(state='active')
daltonButton.config(state='active')
# Anzeigen des Bildes
tkimage = ImageTk.PhotoImage(im)
myvar = tk.Label(SB.frame, image=tkimage)
myvar.image = tkimage
myvar.grid(columnspan=5)
# Einspeichern der Path-Informationen
global img, rows, cols, kanaele
img = cv2.imread(path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
rows, cols, kanaele = img.shape
def showImage(img, rows, cols, kanaele, sim_faktor, sim_kind, sim_art):
color_blindness = Dyschromasie(img, rows, cols, kanaele, sim_faktor, sim_kind)
if sim_art == 'Sim':
color_blindness_mat = np.copy(color_blindness.Simulate()).astype('uint8')
else:
color_blindness_mat = np.copy(color_blindness.daltonize()).astype('uint8')
T = tk.Text(SB.frame, height=1, width=15)
T.grid(columnspan=5)
print_string = sim_kind + ' ' + str(round(color_blindness.sim_faktor * 100)) + "%"
T.insert('current', print_string)
conv_SimulationPic = ImageTk.PhotoImage(image=PIL.Image.fromarray(color_blindness_mat))
sim_pic = tk.Label(SB.frame, image=conv_SimulationPic)
sim_pic.Image = conv_SimulationPic
sim_pic.grid(columnspan=5)
def simulate():
global img, rows, cols, kanaele, sim_pro, sim_deut, sim_tri
if sim_deut.get():
showImage(img, rows, cols, kanaele, simGrad.get()/100, 'Deuteranop', 'Sim')
elif sim_tri.get():
showImage(img, rows, cols, kanaele, simGrad.get() / 100, 'Tritanop', 'Sim')
elif sim_pro.get():
showImage(img, rows, cols, kanaele, simGrad.get() / 100, 'Protanop', 'Sim')
def daltonize():
global img, rows, cols, kanaele, sim_pro, sim_deut, sim_tri
if sim_deut.get():
showImage(img, rows, cols, kanaele, simGrad.get()/100, 'Deuteranop', 'Dalt')
elif sim_tri.get():
showImage(img, rows, cols, kanaele, simGrad.get() / 100, 'Tritanop', 'Dalt')
elif sim_pro.get():
showImage(img, rows, cols, kanaele, simGrad.get() / 100, 'Protanop', 'Dalt')
browseButton = tk.Button(SB.frame, text="Browse", width=25, command=browse, bg='light blue')
browseButton.grid(column=0, row=0, columnspan=1)
simulateButton = tk.Button(SB.frame, text="Simulate", width=25, command=simulate, bg='light blue')
simulateButton.grid(column=1, row=0, columnspan=1)
simulateButton.config(state='disabled')
daltonButton = tk.Button(SB.frame, text="Daltonize", width=25, command=daltonize, bg='light blue')
daltonButton.grid(column=2, row=0, columnspan=1)
daltonButton.config(state='disabled')
checkButton_p = tk.Checkbutton(SB.frame, text="Protanop", variable=sim_pro, onvalue=1, offvalue=0, height=5, width=20)
checkButton_d = tk.Checkbutton(SB.frame, text="Deutanop", variable=sim_deut, onvalue=1, offvalue=0, height=5, width=20)
checkButton_t = tk.Checkbutton(SB.frame, text="Tritanop", variable=sim_tri, onvalue=1, offvalue=0, height=5, width=20)
checkButton_p.grid(column=0, row=2)
checkButton_d.grid(column=1, row=2)
checkButton_t.grid(column=2, row=2)
SB.update()
root.mainloop()
+14 -16
View File
@@ -2,17 +2,17 @@ import cv2 # OpenCV fuer Bildbearbeitung
import tkinter # Zum Erstellen von GUIs import tkinter # Zum Erstellen von GUIs
import numpy as np # Numpy Import import numpy as np # Numpy Import
import sys import sys
from PIL import Image, ImageTk #Wichtig zum Anzeigen der Bilder im GUI
# Einlesen des Bildes # Einlesen des Bildes
script_dir = sys.path[0] script_dir = sys.path[0]
path = script_dir[:-4] + "Beispielbilder\grocery_store.jpg" path = script_dir[:-4] + "Beispielbilder\lena.jpg"
image = cv2.cvtColor(cv2.imread(path),cv2.COLOR_BGR2RGB) # Einlesen des Bildes (noch hardcodiert, sollte dann in GUI gehen) image = cv2.imread(path) # Einlesen des Bildes (noch hardcodiert, sollte dann in GUI gehen)
rows = image.shape[0] # Auslesen der Zeilenanzahl rows = image.shape[0] # Auslesen der Zeilenanzahl
cols = image.shape[1] # Auslesen der Spaltenanzahl cols = image.shape[1] # Auslesen der Spaltenanzahl
kanaele = image.shape[2] # Auslesen der Kanaele (3 fuer RGB, 1 fuer Graubild) kanaele = image.shape[2] # Auslesen der Kanaele (3 fuer RGB, 1 fuer Graubild)
def gammaCorrection(v): def gammaCorrection(v):
if v <= 0.04045 * 255: if v <= 0.04045 * 255:
return float(((v / 255) / 12.92)) return float(((v / 255) / 12.92))
@@ -32,6 +32,7 @@ def reverseGammaCorrection(v_reverse):
print("Ungültiger Wert!!!") print("Ungültiger Wert!!!")
return 1 return 1
cb_image = np.copy(image) # Kopie des Bildarrays cb_image = np.copy(image) # Kopie des Bildarrays
cb_image = cb_image.astype('float64') # Casting des Arrays auf Float cb_image = cb_image.astype('float64') # Casting des Arrays auf Float
@@ -61,7 +62,14 @@ T_reversed = np.array([[5.47221206, -4.6419601, 0.16963708],
[-1.1252419, 2.29317094, -0.1678952], [-1.1252419, 2.29317094, -0.1678952],
[0.02980165, -0.19318073, 1.16364789]]) [0.02980165, -0.19318073, 1.16364789]])
S_p = np.array([[0, 1.05118294, -0.05116099], #Simulationsmatrix fuer Protanopie # Multiplikation der einzelnen Pixelwerte
for i in range(rows):
for j in range(cols):
cb_image[i, j] = T.dot(cb_image[i, j])
# Simulationsmatrizen fuer Protanop
S_b = np.array([[0, 1.05118294, -0.05116099], #Simulationsmatrix fuer Protanopie
[0, 1, 0], [0, 1, 0],
[0, 0, 1]]) [0, 0, 1]])
@@ -73,21 +81,11 @@ S_t = np.array([[1, 0, 0], #Simulationsmatrix fuer Tritanopi
[0, 1, 0], [0, 1, 0],
[-0.86744736, 1.86727089, 0]]) [-0.86744736, 1.86727089, 0]])
#choice = input("Bitte geben Sie den Typ der zu simulierenden Farbblindheit an:(B,D,T)")
#Multiplikation der einzelnen Pixel
for i in range(rows):
for j in range(cols):
cb_image[i,j] = T_reversed.dot(S_t).dot(T).dot(cb_image[i,j])
sim_image = np.copy(cb_image)
sim_image = sim_image.astype('uint8')
#Rücktransformation der Gammawerte
for i in range(rows):
for j in range(cols):
for x in range(3):
sim_image[i, j, x] = reverseGammaCorrection(cb_image[i, j, x])
cv2.namedWindow("Display") # Displaywindow erstellen cv2.namedWindow("Display") # Displaywindow erstellen
cv2.imshow("Display", cv2.cvtColor(sim_image,cv2.COLOR_RGB2BGR)) # Bild zeigen cv2.imshow("Display", image) # Bild zeigen
cv2.waitKey(0) # Fenster offen halten cv2.waitKey(0) # Fenster offen halten
-126
View File
@@ -1,126 +0,0 @@
import numpy as np
import cv2
def createGammaLookup():
return np.array([removeGammaCorrection(i) for i in np.arange(0, 256)]).astype("float64")
def createReverseGammaLookup():
return np.array([applyGammaCorrection(i / 255) for i in np.arange(0.0, 256.0)]).astype("uint8")
def removeGammaCorrection(v):
if v <= 0.04045 * 255:
return (v / 255) / 12.92
elif v > 0.04045 * 255:
return (((v / 255) + 0.055) / 1.055) ** 2.4
def applyGammaCorrection(v_reverse):
if abs(v_reverse) <= 0.0031308:
return round(255 * (12.92 * abs(v_reverse)))
else:
if (round(255 * (1.055 * abs(v_reverse) ** 0.41666 - 0.055))) > 255:
return 255 - (round(255 * (1.055 * abs(v_reverse) ** 0.41666 - 0.055)) - 255)
else:
return round(255 * (1.055 * abs(v_reverse) ** 0.41666 - 0.055))
class Dyschromasie:
cb_image = np.array([]).astype('float64')
sim_image = np.array([]).astype('int64')
def __init__(self, img_mat=np.array([]), rows=0, cols=0, kanaele=0, sim_faktor=0, sim_kind='Deuteranop'):
self.rows = rows
self.cols = cols
self.kanaele = kanaele
self.img_mat = img_mat
self.sim_faktor = sim_faktor
self.sim_kind = sim_kind
T = np.array([[0.31399022, 0.63951294, 0.04649755],
[0.15537241, 0.75789446, 0.08670142],
[0.01775239, 0.10944209, 0.87256922]])
T_reversed = np.array([[5.47221206, -4.6419601, 0.16963708],
[-1.1252419, 2.29317094, -0.1678952],
[0.02980165, -0.19318073, 1.16364789]])
def Simulate(self):
removeGammaCorrectionLUT = createGammaLookup()
if self.sim_kind == 'Protanop':
sim_mat = np.array([[(1 - self.sim_faktor), 1.05118294 * self.sim_faktor, -0.05116099 * self.sim_faktor],
[0, 1, 0],
[0, 0, 1]])
elif self.sim_kind == 'Deuteranop':
sim_mat = np.array([[1, 0, 0],
[0.9513092 * self.sim_faktor, (1 - self.sim_faktor), 0.04866992 * self.sim_faktor],
[0, 0, 1]])
elif self.sim_kind == 'Tritanop':
sim_mat = np.array([[1, 0, 0],
[0, 1, 0],
[-0.86744736 * self.sim_faktor, 1.86727089 * self.sim_faktor, (1 - self.sim_faktor)]])
# Gammakorrektur durchfuehren
self.cb_image = np.copy(self.img_mat).astype('float64')
self.cb_image = cv2.LUT(self.img_mat, removeGammaCorrectionLUT)
rechen_Mat = np.copy(self.T_reversed.dot(sim_mat).dot(self.T))
# Einzelne Pixelwertberechnung
for i in range(self.rows):
for j in range(self.cols):
self.cb_image[i, j] = rechen_Mat.dot(self.cb_image[i, j])
self.sim_image = np.copy(self.cb_image)
self.sim_image = self.sim_image.astype('int64')
# Rücktransformation der Gammawerte
for i in range(self.rows):
for j in range(self.cols):
for x in range(3):
self.sim_image[i, j, x] = applyGammaCorrection(self.cb_image[i, j, x])
return self.sim_image.astype('uint8')
def daltonize(self):
err2mod = np.array([[0, 0, 0], [0.7, 1, 0], [0.7, 0, 1]])
simulated_image = self.Simulate()
E = np.copy(simulated_image).astype('int32')
for i in range(self.rows):
for j in range(self.cols):
for x in range(3):
E[i, j, x] = abs(int(self.img_mat[i, j, x]) - int(simulated_image[i, j, x]))
ERR = np.zeros_like(self.img_mat).astype('int32')
for i in range(self.rows):
for j in range(self.cols):
err = E[i, j, :3]
ERR[i, j, :3] = np.dot(err2mod, err)
dtpn = np.copy(self.img_mat).astype('int32')
for i in range(self.rows):
for j in range(self.cols):
for x in range(3):
dtpn[i, j, x] = abs(int(ERR[i, j, x]) + int(self.img_mat[i, j, x]))
for i in range(self.rows):
for j in range(self.cols):
dtpn[i, j, 0] = max(0, dtpn[i, j, 0])
dtpn[i, j, 0] = min(255, dtpn[i, j, 0])
dtpn[i, j, 1] = max(0, dtpn[i, j, 1])
dtpn[i, j, 1] = min(255, dtpn[i, j, 1])
dtpn[i, j, 2] = max(0, dtpn[i, j, 2])
dtpn[i, j, 2] = min(255, dtpn[i, j, 2])
result = dtpn.astype('uint8')
dalt = Dyschromasie(result, self.rows, self.cols, self.kanaele, self.sim_faktor, self.sim_kind)
return dalt.Simulate()
-59
View File
@@ -1,59 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
class AutoScrollbar(tk.Scrollbar):
# A scrollbar that hides itself if it's not needed.
# Only works if you use the grid geometry manager!
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
# grid_remove is currently missing from Tkinter!
self.tk.call("grid", "remove", self)
else:
self.grid()
tk.Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
class ScrollFrame:
def __init__(self, master):
self.vscrollbar = AutoScrollbar(master)
self.vscrollbar.grid(row=0, column=1, sticky='ns')
self.hscrollbar = AutoScrollbar(master, orient='horizontal')
self.hscrollbar.grid(row=1, column=0, sticky='ew')
self.canvas = tk.Canvas(master, yscrollcommand=self.vscrollbar.set,
xscrollcommand=self.hscrollbar.set)
self.canvas.grid(row=0, column=0, sticky='nsew')
self.vscrollbar.config(command=self.canvas.yview)
self.hscrollbar.config(command=self.canvas.xview)
# make the canvas expandable
master.grid_rowconfigure(0, weight=1)
master.grid_columnconfigure(0, weight=1)
# create frame inside canvas
self.frame = tk.Frame(self.canvas)
self.frame.rowconfigure(1, weight=1)
self.frame.columnconfigure(1, weight=1)
# update the frame
self.frame.bind("<Configure>", self.reset_scrollregion)
def reset_scrollregion(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox('all'))
def update(self):
self.canvas.create_window(0, 0, anchor='nw', window=self.frame)
self.frame.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if self.frame.winfo_reqwidth() != self.canvas.winfo_width():
# update the canvas's width to fit the inner frame
self.canvas.config(width = self.frame.winfo_reqwidth())
if self.frame.winfo_reqheight() != self.canvas.winfo_height():
# update the canvas's width to fit the inner frame
self.canvas.config(height = self.frame.winfo_reqheight())
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB