Matlablib a colorbar – Python – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Matlablib a colorbar – Python – Fórum – Programujte.comMatlablib a colorbar – Python – Fórum – Programujte.com

 

oxidián0
Grafoman
26. 4. 2024   #1
-
0
-

Nevíte proč mi první příklad na této stránce hlásí chybu?

https://newbedev.com/matplotlib-add-colorbar-to-a-sequence-of-line-plots

fig, ax = plt.subplots(dpi=100)
# Make dummie mappable
dummie_cax = ax.scatter(c, c, c=c, cmap=cmap)
# Clear axis
ax.cla()
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap(i))
fig.colorbar(dummie_cax, ticks=c)


Exception has occurred: ValueErrorUnable to determine Axes to steal space for Colorbar. Either provide the *cax* argument to use as the Axes for the Colorbar, provide the *ax* argument to steal space from it, or add *mappable* to an Axes.
File "testik.py", line 19, in <module> fig.colorbar(dummie_cax, ticks=c) ValueError: Unable to determine Axes to steal space for Colorbar. Either provide the *cax* argument to use as the Axes for the Colorbar, provide the *ax* argument to steal space from it, or add *mappable* to an Axes.
 

Vypadat to, že to je stará syntaxe či co

Nahlásit jako SPAM
IP: 78.45.196.–
gna
~ Anonymní uživatel
1860 příspěvků
26. 4. 2024   #2
-
+1
-
Zajímavé

Změněné řádky jsou ty zakomentované s #old#

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

n_lines = 5
x = np.linspace(0, 10, 100)
y = np.sin(x[:, None] + np.pi * np.linspace(0, 1, n_lines))
c = np.arange(1, n_lines + 1)

#old# cmap = mpl.cm.get_cmap('jet', n_lines)
cmap = mpl.colormaps['jet'].resampled(n_lines)

fig, ax = plt.subplots(dpi=100)
dummie_cax = ax.scatter(c, c, c=c, cmap=cmap)
ax.cla()
for i, yi in enumerate(y.T):
    ax.plot(x, yi, c=cmap(i))
#old# fig.colorbar(dummie_cax, ticks=c)
fig.colorbar(dummie_cax, ticks=c, ax=ax)
plt.show()
Nahlásit jako SPAM
IP: 213.211.51.–
oxidián0
Grafoman
28. 4. 2024   #3
-
0
-

Dík. A mám dotaz na tento kód:

import numpy as np
import matplotlib.pyplot as plt

# Nastavení obecných dat
N = 37
x, y = np.mgrid[:N, :N]
Z = (np.cos(x * 0.2) + 1) * 127.5  # Posunutí a změna rozsahu na 0-255
Z += (np.sin(y * 0.3) + 1) * 127.5  # Posunutí a změna rozsahu na 0-255

# Zobrazení dat s použitím colormapy 'hsv' a nastavením vmin a vmax
fig, ax = plt.subplots(figsize=(3.7, 3))
pos_neg_clipped = ax.imshow(Z, cmap='hsv', vmin=0, vmax=360)

# Přidání minorních značek na colorbaru
color_bar = fig.colorbar(pos_neg_clipped, ax=ax, extend='both')
color_bar.minorticks_on()

plt.show()

Je možné udělat takovou věc? Když místo toho vykreslení načtu obrázek a kliknu na obrázek, aby se mi ten colorbar "přiblížil" jen na barvy v určitém rozsahu? HSV odstíny 0-360° je tam asi 7 barev na té škále, takže když bych měl vybrán odstín s hodnotou u 270° zobrazí se mi rozsah řekněme +/- 30° (tj. vmin=240, vmax=300), ale tomu by musely odpovídat i ty barvy. Ještě lepší by bylo, kdyby si uživatel mohl ten rozsah nadefinovat sám... třeba chce najít v obrázku barvy, které jsou v určitém rozsahu, a ten rozsah nebude zrovna přesně 240 až 300 ale bude třeba malinko jiný. Třeba zelená má na té škále větší rozsah než ty ostatní barvy.

A dále by mě zajímalo jestli je možné mít tam ty colorbary tři jeden pro hue, druhý pro saturaci, třetí pro brightness. A zároveň by tam mohl mít u každého vidět nějaký vhodný rozsah - ty ticky si tam nastavit umím, ale neuměl bych třeba udělat, aby si mohl nastavit vlastní rozsah hledaného minima a maxima... Co konkrétně mě zajímalo na mapách cz, když vybereš nějakou autobusovou trasu, uděláš si screenshot a pak klikneš na ten obrázek screenshotu na tu modrou trasu, tak chci identifikovat ty barvy té trasy na obrázku (abych s tím potom mohl dál pracovat jako určit tu trasu, jak vede ta cesta). Tím dávám příklad jak to chci používat a kam směřuji, protože si chci napsat takovou aplikaci, která mi umožní si snadno nakonfigurovat hledané barvy (a pak bych si mohl implementovat i ukládání těchto hodnot).

Nahlásit jako SPAM
IP: 94.113.178.–
gna
~ Anonymní uživatel
1860 příspěvků
28. 4. 2024   #4
-
0
-

Těch colorbarů můžeš mít několik a intuitivně bych řekl, že by velká část toho mohla jít implementovat čistě matplotem, ale nevím, já matplotlib, používám, jen když chci pěkné grafy a moc si ho nepřizpůsobuju. Ale samozřejmě zase si můžeš udělat nějaký slider bokem, který bude ten obrázek filtrovat přesně, jak chceš.

Nahlásit jako SPAM
IP: 213.211.51.–
oxidián0
Grafoman
28. 4. 2024   #5
-
0
-

Napadlo mě hledat scale a našel jsem tento argument:

matplotlib.pyplot.colorbar(mappable=None, cax=None, ax=None, **kwargs)[source]


Other Parameters:

    boundaries, valuesNone or a sequence

        If unset, the colormap will be displayed on a 0-1 scale. If sequences, values must have a length 1 less than boundaries. For each region delimited by adjacent entries in boundaries, the color mapped to the corresponding value in values will be used. Normally only useful for indexed colors (i.e. norm=NoNorm()) or other unusual circumstances.

Tak to by bylo užitečné.

Ohledně multi colorbarů jsem našel toto, ale není to kompletní kód:

https://stackoverflow.com/questions/63953589/matplotlib-colorbar-non-linear

Nahlásit jako SPAM
IP: 89.177.85.–
gna
~ Anonymní uživatel
1860 příspěvků
28. 4. 2024   #6
-
0
-

No on zobrazí cokoliv, ale na to filtrování se mi zdá, že chceš něco složitějšího a to jsem myslel si pořešit sám a netrápit se s matplotem pro nic jiného než zobrazování. 

import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.widgets import RangeSlider
import tkinter as tk

def main():
    root = tk.Tk()

    filename = tk.filedialog.askopenfilename(
        filetypes=(
            ("Images", (".bmp", ".jpg", ".png")),
            ("All files", "*.*"),
        )
    )
    if not filename:
        return
    image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
    image_r, image_g, image_b = cv2.split(image)

    fig, (ax1, ax2) = plt.subplots(2, 1)
    im1 = ax1.imshow(image)
    im2 = ax2.imshow(image)
    fig.subplots_adjust(bottom=0.30)

    rslider_ax = fig.add_axes([0.20, 0.20, 0.60, 0.03])
    rslider = RangeSlider(rslider_ax, "R", 0, 255, valinit=(0, 255))
    gslider_ax = fig.add_axes([0.20, 0.15, 0.60, 0.03])
    gslider = RangeSlider(gslider_ax, "G", 0, 255, valinit=(0, 255))
    bslider_ax = fig.add_axes([0.20, 0.1, 0.60, 0.03])
    bslider = RangeSlider(bslider_ax, "B", 0, 255, valinit=(0, 255))

    fig_canvas = FigureCanvasTkAgg(fig, root)
    fig_canvas.get_tk_widget().pack(expand=True, fill=tk.BOTH)
    fig_toolbar = NavigationToolbar2Tk(fig_canvas, root, pack_toolbar=False)
    fig_toolbar.pack(fill=tk.X)

    def update(val):
        rl, rh = rslider.val
        gl, gh = gslider.val
        bl, bh = bslider.val

        mask_r = cv2.inRange(image_r, rl, rh)
        mask_g = cv2.inRange(image_g, gl, gh)
        mask_b = cv2.inRange(image_b, bl, bh)
        mask = cv2.bitwise_and(cv2.bitwise_and(mask_r, mask_g), mask_b)

        masked = cv2.bitwise_and(image, image, mask=mask)
        im2.set_data(masked)
        fig.canvas.draw_idle()

    rslider.on_changed(update)
    gslider.on_changed(update)
    bslider.on_changed(update)

    def pick_color(event):
        if event.inaxes is not ax1:
            return
        x, y = int(event.xdata), int(event.ydata)
        r, g, b = map(float, image[y, x])
        rslider.set_val((r-20, r+20))
        gslider.set_val((g-20, g+20))
        bslider.set_val((b-20, b+20))

    fig_canvas.mpl_connect("button_press_event", pick_color)

    root.mainloop()


if __name__ == "__main__":
    main()
Nahlásit jako SPAM
IP: 213.211.51.–
oxidián0
Grafoman
28. 4. 2024   #7
-
0
-

Dík za kód. Jak říkám TKinter je pro mě zatím španělská vesnice. Na to budu potřebovat čas. Zatím jsem se díval jen na ty colorbary. Myslel, jsem že to tam půjde přiblížit ta colormapa ale nevím jestli to dělám špatně nebo jsem ten manuál vůbec nepochopil a nejde to. Chtěl jsem druhý colorbar nastavit na barevný přechod v rozsahu hodnot 1-180 a druhý colorbar na 181-360° a obrázek zobrazit na canvasu. Ale nepodařilo se.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from PIL import Image

# Load the image
img = Image.open('blonde 2.png')


# Convert the image to numpy array and scale values from 0-255 to 1-360
ZValues = np.array(img) * (360 / 255)

# Define colormap
cmap = plt.cm.hsv

# Define bins and normalize for the first colorbar
bounds = np.linspace(1, 360, 50)
ticks_colorbar1=[1, 180, 360]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)

# Create plot
fig, ax = plt.subplots(figsize=(8, 6))

# Plot the image on the left side of the canvas
ax.imshow(ZValues, cmap='gray')

# Create the first colorbar
ax2 = fig.add_axes([0.87, 0.12, 0.04, 0.75])
ticks_colorbar2=[1, 180]
cb = mpl.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm, spacing='uniform', ticks=ticks_colorbar2, boundaries=bounds, format='%1i')
cb.set_label('ZValues', fontsize=7, weight="bold", rotation=270, labelpad=14)

# Define bins and normalize for the second colorbar (1-180)
bounds_colorbar2 = np.linspace(1, 180, 18)
norm2 = mpl.colors.BoundaryNorm(bounds_colorbar2, cmap.N)

# Create the second colorbar
ax3 = fig.add_axes([0.92, 0.12, 0.04, 0.75])
ticks_colorbar3=[181, 360]
cb2 = mpl.colorbar.ColorbarBase(ax3, cmap=cmap, norm=norm2, spacing='uniform', ticks=ticks_colorbar3, boundaries=bounds_colorbar2, format='%1i')
cb2.set_label('ZValues', fontsize=7, weight="bold", rotation=270, labelpad=14)

# Define bins and normalize for the third colorbar (181-360)
bounds_colorbar3 = np.linspace(181, 360, 18)
norm3 = mpl.colors.BoundaryNorm(bounds_colorbar3, cmap.N)

# Create the third colorbar
ax4 = fig.add_axes([0.97, 0.12, 0.04, 0.75])
cb3 = mpl.colorbar.ColorbarBase(ax4, cmap=cmap, norm=norm3, spacing='uniform', ticks=bounds_colorbar3, boundaries=bounds_colorbar3, format='%1i')
cb3.set_label('ZValues', fontsize=7, weight="bold", rotation=270, labelpad=14)

plt.show()
Nahlásit jako SPAM
IP: 89.177.85.–
oxidián0
Grafoman
28. 4. 2024   #8
-
0
-

#6 gna
Tvůj kód jsem vylepšil o tmavé schéma a uložení konfiguráku s názvem souboru.

Ale celkově mi to jede pomalu. Strašně pomalu. CPU dvoujádrový Celeron asi z roku 2009.

Nejde to pohodlně naklikat. Je to tím, že ten obrázek je 1024x1024 velký?

Jak to dělá Photoshop když se vybírají barvy pracuje to strašně rychle... Já vím, že ImageMagick x64 byl taky dost pomalý ještě na starém PC z roku 2004 tam byl taky Pentium IV když jsem to zkoušel. Není to tím, že matlab pracuje s tím x64 a tak to jede mnohem pomaleji než Photoshop na x32 Windows XP? Protože Photoshop na novějších systémech jsem nikdy nezkoušel.

import os
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.widgets import RangeSlider
import tkinter as tk
from tkinter import filedialog
from ttkthemes import ThemedTk

def main():
    root = ThemedTk(theme="black")
    root.title("Image Analyzer")

    filename = filedialog.askopenfilename(
        filetypes=(
            ("Images", (".bmp", ".jpg", ".png")),
            ("All files", "*.*"),
        )
    )
    if not filename:
        root.destroy()
        return

    def on_close():
        # Zde se soubor smaže při ukončení programu
        os.remove(filename)
        root.destroy()

    root.protocol("WM_DELETE_WINDOW", on_close)

    image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
    image_r, image_g, image_b = cv2.split(image)

    fig, (ax1, ax2) = plt.subplots(2, 1)  # rozložení: 2 řádky, 1 sloupec
    im1 = ax1.imshow(image)
    im2 = ax2.imshow(image)
    fig.subplots_adjust(bottom=0.30)

    # matplotlib.widgets / RangeSlider
    rslider_ax = fig.add_axes([0.20, 0.20, 0.60, 0.03])
    rslider = RangeSlider(rslider_ax, "R", 0, 255, valinit=(0, 255))
    gslider_ax = fig.add_axes([0.20, 0.15, 0.60, 0.03])
    gslider = RangeSlider(gslider_ax, "G", 0, 255, valinit=(0, 255))
    bslider_ax = fig.add_axes([0.20, 0.1, 0.60, 0.03])
    bslider = RangeSlider(bslider_ax, "B", 0, 255, valinit=(0, 255))

    fig_canvas = FigureCanvasTkAgg(fig, master=root)
    fig_canvas.get_tk_widget().pack(expand=True, fill=tk.BOTH)
    fig_toolbar = NavigationToolbar2Tk(fig_canvas, root, pack_toolbar=False)
    fig_toolbar.pack(fill=tk.X)

    def update(val):
        rl, rh = rslider.val
        gl, gh = gslider.val
        bl, bh = bslider.val

        mask_r = cv2.inRange(image_r, rl, rh)
        mask_g = cv2.inRange(image_g, gl, gh)
        mask_b = cv2.inRange(image_b, bl, bh)
        mask = cv2.bitwise_and(cv2.bitwise_and(mask_r, mask_g), mask_b)

        masked = cv2.bitwise_and(image, image, mask=mask)
        im2.set_data(masked)
        fig.canvas.draw_idle()

    rslider.on_changed(update)
    gslider.on_changed(update)
    bslider.on_changed(update)

    def pick_color(event):
        if event.inaxes is not ax1:
            return
        x, y = int(event.xdata), int(event.ydata)
        r, g, b = map(float, image[y, x])
        rslider.set_val((r - 20, r + 20))
        gslider.set_val((g - 20, g + 20))
        bslider.set_val((b - 20, b + 20))

    fig_canvas.mpl_connect("button_press_event", pick_color)

    root.mainloop()


if __name__ == "__main__":
    main()
Nahlásit jako SPAM
IP: 89.177.85.–
gna
~ Anonymní uživatel
1860 příspěvků
29. 4. 2024   #9
-
0
-

Ta funkce `pick_color`, když nastavuje ty slidery, tak každý vyvolá update, takže se to přepočítá třikrát.

Ten slider sám je zalagovaný i když nic nedělá, takže jeho ruční posunování je asi hlavně pocitově pomalé než, že by to nestíhalo. Ale možná to opravu nestíhá. Nevím jak ty události fungují, možná by pomohlo přidat test, jestli se hodnota těch sliderů změnila. (Protože teď ten update ignoruje ten parametr s hodnotou a počítá pro aktuální hodnoty, tak by tak možná šlo přeskočit nějaké staré eventy.)

Když se generují ty masky, tak to vždycky alokuje nové pole, ale ty funkce umožňují ukládat výsledek i do předalokovaného pole. A tak...

A celkově nakonec atualizace toho matplotu je asi poměrně pomalá. Klasika, funguje to tak, jak to je naprogramované.

Nahlásit jako SPAM
IP: 213.211.51.–
oxidián0
Grafoman
29. 4. 2024   #10
-
0
-

#9 gna
Vzhledem k tomu že ten posuvník je zalagovaný tak asi nemá smysl používat ten posuvník matlabu a bylo by lepší tam importovat ten od TKInteru - si myslím. Ještě jsem to nezkoušel. Napadlo mě, že se možná něco updatuje příliš často, ale ten obrázek od LLM má 1024x1024 ... screenshot mapy 800x600 ale testuju to na tom větším. Včera večer jsem už nestihl se tomu věnovat měl jsem nějaký problémy s linuxem, ale už mám vyřešeno.

Nebo prostě nedělat posuvníky, a kdyby to šlo udělat ty tři škály colorbar a udělat událost jen onclick. To netuším jestli jde, ale myslím že minor ticks bych mohl vynechat a klikem bych mohl přidat nový tick (starý by zmizel).

To jsou zatím jen úvahy, zatím jsem nepřišel na to jak udělat ten scale do colorbaru, aby se zobrazily třeba barvy cmap='hsv' v rozsahu červené až žluté

Vykreslování ticků pokud člověk dá třeba 100 tak je hodně pomalé, to jo.

Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
29. 4. 2024   #11
-
0
-

#10 oxidián
Chápu to správně, že první aktulizace (kod funkce update) by vypadala takto?

    # Update the masks in place
    cv2.inRange(image_r, rl, rh, dst=mask_r)
    cv2.inRange(image_g, gl, gh, dst=mask_g)
    cv2.inRange(image_b, bl, bh, dst=mask_b)

    # Update the combined mask
    cv2.bitwise_and(mask_r, mask_g, mask=mask_b)

    # Update the masked image
    cv2.bitwise_and(image, image, mask=mask_b)

Nevím kde mám najít manuál k tomu cv2,

našel jsem to jen v zápisu c/c++

https://docs.opencv.org/3.4/da/d97/tutorial_threshold_inRange.html

 // Detect the object based on HSV Range Values
 inRange(frame_HSV, Scalar(low_H, low_S, low_V), Scalar(high_H, high_S, high_V), frame_threshold);

Ale ani tam nevidím syntaxy.

Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
29. 4. 2024   #12
-
0
-

TADY JE AKTUALIZOVANÝ KÓD. Myslel jsem že chatGPT tam už to ukládání souboru do konfiguráku implementoval (zatím ještě neprogramuju já). Náhled, který se mi otevřel teď je mapka 800x600 a ta je dost malá, takže moc nevidím co je na obrázku... Ale s touto malou mapkou to jede dobře. Taky bych to chtěl předělat do HSV, akorád nevím co dřív udělat.

import os
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.widgets import RangeSlider
import tkinter as tk
from tkinter import filedialog
from ttkthemes import ThemedTk
import configparser

def save_filename(filename):
    config = configparser.ConfigParser()
    config['DEFAULT'] = {'filename': filename}
    with open('config.cfg', 'w') as configfile:
        config.write(configfile)

def load_filename():
    config = configparser.ConfigParser()
    config.read('config.cfg')
    return config['DEFAULT']['filename']

def load_image():
    try:
        filename = load_filename()
    except KeyError:
        filename = filedialog.askopenfilename()
        save_filename(filename)

    image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
    return image

def main():
    root = ThemedTk(theme="black")
    root.title("Image Analyzer")

    image = load_image()
    image_r, image_g, image_b = cv2.split(image)

    fig, (ax1, ax2) = plt.subplots(2, 1)  # layout: 2 rows, 1 column
    im1 = ax1.imshow(image)
    im2 = ax2.imshow(image)
    fig.subplots_adjust(bottom=0.30)

    # matplotlib.widgets / RangeSlider
    rslider_ax = fig.add_axes([0.20, 0.20, 0.60, 0.03])
    rslider = RangeSlider(rslider_ax, "R", 0, 255, valinit=(0, 255))
    gslider_ax = fig.add_axes([0.20, 0.15, 0.60, 0.03])
    gslider = RangeSlider(gslider_ax, "G", 0, 255, valinit=(0, 255))
    bslider_ax = fig.add_axes([0.20, 0.1, 0.60, 0.03])
    bslider = RangeSlider(bslider_ax, "B", 0, 255, valinit=(0, 255))

    # Create masks beforehand
    mask_r = cv2.inRange(image_r, 0, 255)
    mask_g = cv2.inRange(image_g, 0, 255)
    mask_b = cv2.inRange(image_b, 0, 255)

    fig_canvas = FigureCanvasTkAgg(fig, master=root)
    fig_canvas.get_tk_widget().pack(expand=True, fill=tk.BOTH)
    fig_toolbar = NavigationToolbar2Tk(fig_canvas, root, pack_toolbar=False)
    fig_toolbar.pack(fill=tk.X)

    def update(val):
        rl, rh = rslider.val
        gl, gh = gslider.val
        bl, bh = bslider.val

        # Update the masks in place
        cv2.inRange(image_r, rl, rh, dst=mask_r)
        cv2.inRange(image_g, gl, gh, dst=mask_g)
        cv2.inRange(image_b, bl, bh, dst=mask_b)

        # Update the combined mask
        cv2.bitwise_and(mask_r, mask_g, mask=mask_b)

        # Update the masked image
        masked = cv2.bitwise_and(image, image, mask=mask_b)
        im2.set_data(masked)
        fig.canvas.draw_idle()

    rslider.on_changed(update)
    gslider.on_changed(update)
    bslider.on_changed(update)

    def pick_color(event):
        if event.inaxes is not ax1:
            return
        x, y = int(event.xdata), int(event.ydata)
        r, g, b = map(float, image[y, x])
        rslider.set_val((r - 20, r + 20))
        gslider.set_val((g - 20, g + 20))
        bslider.set_val((b - 20, b + 20))

    fig_canvas.mpl_connect("button_press_event", pick_color)

    root.mainloop()

if __name__ == "__main__":
    main()
Nahlásit jako SPAM
IP: 94.113.179.–
gna
~ Anonymní uživatel
1860 příspěvků
29. 4. 2024   #13
-
0
-

Já jsem zkusil vyhodit ten matplotlib. Jen jsem to na zkoušku namatlal a pořád to asi zbytečně pracuje s velkým obrázkem, který se jen pro zobrazení škáluje, ale litá to pěkně rychle.

To cv2 je jen binding 1:1 k té Céčkovské knihovně (cv) a v její dokumentaci je vždycky i signatura funkce v Pythonu a fungování je úplně stejné.

import cv2
import PIL.Image, PIL.ImageTk
import tkinter.filedialog
import tkinter as tk


def clamp(val, minval, maxval):
    val = max(val, minval)
    val = min(val, maxval)
    return val


def remap_range(val, fromrange, torange):
    frmin, frmax = fromrange
    tomin, tomax = torange
    frsize = frmax - frmin + 1
    tosize = tomax - tomin + 1
    delta = (val - frmin) / frsize
    return tomin + tosize * delta


def fit_size(w1, h1, w2, h2):
    scale = min(w2 / w1, h2 / h1)
    return int(w1 * scale), int(h1 * scale)


class Box:
    def __init__(self, x1, y1, x2, y2):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2

    @property
    def box(self):
        return self.x1, self.y1, self.x2, self.y2

    @property
    def l(self):
        return self.x1

    @property
    def r(self):
        return self.x2

    @property
    def lr(self):
        return self.x1, self.x2

    @property
    def t(self):
        return self.y1

    @property
    def b(self):
        return self.y2

    @property
    def tb(self):
        return self.y1, self.y2

    @property
    def w(self):
        return self.x2 - self.x1 + 1

    @property
    def h(self):
        return self.y2 - self.y1 + 1

    @property
    def size(self):
        return self.w, self.h


class RangeSlider(tk.Frame):
    def __init__(self, master, valrange, valinit, *args, **kwargs):
        tc = kwargs.pop("troughcolor", None)
        super().__init__(master, *args, **kwargs)
        self.valrange = valrange
        self.lo = tk.Scale(
            self,
            from_=valrange[0],
            to=valrange[1],
            orient=tk.HORIZONTAL,
            troughcolor=tc,
            command=self._lo_changed,
        )
        self.hi = tk.Scale(
            self,
            from_=valrange[0],
            to=valrange[1],
            orient=tk.HORIZONTAL,
            troughcolor=tc,
            command=self._hi_changed,
        )
        self.vals = valinit
        self.set(*self.vals, False)
        self.lo.pack(fill=tk.X)
        self.hi.pack(fill=tk.X)
        self.on_change_callback = None

    def on_change(self, callback):
        self.on_change_callback = callback

    def _lo_changed(self, value):
        lo = int(value)
        hi = max(lo, self.hi.get())
        # change = (lo, hi) != self.vals
        change = lo != self.vals[0]
        self.set(lo, hi, False)
        if change and self.on_change_callback:
            self.on_change_callback(self, lo, hi)

    def _hi_changed(self, value):
        hi = int(value)
        lo = min(hi, self.lo.get())
        # change = (lo, hi) != self.vals
        change = hi != self.vals[1]
        self.set(lo, hi, False)
        if change and self.on_change_callback:
            self.on_change_callback(self, lo, hi)

    def set(self, lo, hi, notify=True):
        lo = clamp(lo, *self.valrange)
        hi = clamp(hi, *self.valrange)
        self.vals = (None, None) if notify else (lo, hi)
        self.lo.set(lo)
        self.hi.set(hi)

    def get(self):
        return self.lo.get(), self.hi.get()


class PixelPicker(tk.Canvas):
    def __init__(self, master, image, *args, **kwargs):
        super().__init__(
            master, *args, **kwargs, bd=0, highlightthickness=0, cursor="tcross"
        )
        self.size = 0, 0
        self.cimg = self.create_image(0, 0, anchor=tk.NW)
        self.crect = self.create_rectangle(0, 0, 0, 0, dash=(10,), state="hidden")
        self.image = image
        self.sbox = Box(0, 0, *image.size)
        self.vbox = None  # prvni vykresleni bude v <Configure>
        self.on_pick_callback = None

        self.bind("<Configure>", self._on_resize)
        self.bind("<Button-1>", self._on_pick)
        self.bind("<Button-3>", self._on_zoom_selstart)
        self.bind("<B3-Motion>", self._on_zoom_selupdate)
        self.bind("<ButtonRelease-3>", self._on_zoom_selend)
        self.bind("<Double-Button-3>", self._on_zoom_reset)

    def set_image(self, image):
        self.image = image
        self.sbox = Box(0, 0, *image.size)
        self.vbox = Box(0, 0, *fit_size(*self.sbox.size, *self.size))
        self._show_image(self.image, self.sbox, self.vbox)

    def on_pick(self, callback):
        self.on_pick_callback = callback

    def _show_image(self, image, sbox, vbox):
        self.vimg = image.resize(vbox.size, PIL.Image.Resampling.NEAREST, sbox.box)
        self.photo = PIL.ImageTk.PhotoImage(image=self.vimg)
        self.itemconfigure(self.cimg, image=self.photo)

    def _zoom_image(self, selbox):
        if selbox.w < 5 or selbox.h < 5:
            return
        x1 = remap_range(selbox.l, self.vbox.lr, self.sbox.lr)
        x2 = remap_range(selbox.r, self.vbox.lr, self.sbox.lr)
        y1 = remap_range(selbox.t, self.vbox.tb, self.sbox.tb)
        y2 = remap_range(selbox.b, self.vbox.tb, self.sbox.tb)
        self.sbox = Box(x1, y1, x2, y2)
        self.vbox = Box(0, 0, *fit_size(*selbox.size, *self.size))
        self._show_image(self.image, self.sbox, self.vbox)

    def _on_resize(self, event):
        self.size = (event.width, event.height)
        self.vbox = Box(0, 0, *fit_size(*self.sbox.size, *self.size))
        self._show_image(self.image, self.sbox, self.vbox)

    def _on_pick(self, event):
        if self.on_pick_callback:
            x = remap_range(event.x, self.vbox.lr, self.sbox.lr)
            y = remap_range(event.y, self.vbox.tb, self.sbox.tb)
            self.on_pick_callback(int(x), int(y))

    def _on_zoom_selstart(self, event):
        self.coords(self.crect, event.x, event.y, event.x, event.y)
        self.itemconfigure(self.crect, state="normal")

    def _on_zoom_selupdate(self, event):
        x1, y1, _, _ = self.coords(self.crect)
        self.coords(self.crect, x1, y1, event.x, event.y)

    def _on_zoom_selend(self, event):
        self.itemconfigure(self.crect, state="hidden")
        x1, y1, x2, y2 = self.coords(self.crect)
        x1 = clamp(x1, self.vbox.l, self.vbox.r)
        x2 = clamp(x2, self.vbox.l, self.vbox.r)
        y1 = clamp(y1, self.vbox.t, self.vbox.b)
        y2 = clamp(y2, self.vbox.t, self.vbox.b)
        self._zoom_image(Box(x1, y1, x2, y2))

    def _on_zoom_reset(self, event):
        self.set_image(self.image)


def main():
    root = tk.Tk()

    # filename = "pink-peony-148225487837A.jpg"
    filename = tk.filedialog.askopenfilename(
        filetypes=(
            ("Images", (".bmp", ".jpg", ".png")),
            ("All files", "*.*"),
        )
    )
    if not filename:
        return

    image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
    image_r, image_g, image_b = cv2.split(image)

    pil_image = PIL.Image.fromarray(image)
    w, h = fit_size(*pil_image.size, 500, 500)

    img_frame = tk.Frame(root)
    im1 = PixelPicker(img_frame, pil_image, width=w, height=h)
    im1.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
    im2 = PixelPicker(img_frame, pil_image, width=w, height=h)
    im2.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
    img_frame.pack(expand=True, fill=tk.BOTH)

    pixel_canvas = tk.Canvas(root, width=100, height=50, background="black")
    pixel_canvas.pack()
    rslider = RangeSlider(root, (0, 255), (10, 20), troughcolor="red")
    rslider.pack(fill=tk.BOTH)
    gslider = RangeSlider(root, (0, 255), (10, 20), troughcolor="green")
    gslider.pack(fill=tk.BOTH)
    bslider = RangeSlider(root, (0, 255), (10, 20), troughcolor="blue")
    bslider.pack(fill=tk.BOTH)

    mask_r = mask_g = mask_b = mask_full = masked = None

    def update(slider, lo, hi):
        nonlocal mask_r, mask_g, mask_b, mask_full, masked

        # mask_r = mask_g = mask_b = mask_full = masked = None

        if mask_r is None or slider is None or slider is rslider:
            rl, rh = rslider.get()
            mask_r = cv2.inRange(image_r, rl, rh, mask_r)
        if mask_g is None or slider is None or slider is gslider:
            gl, gh = gslider.get()
            mask_g = cv2.inRange(image_g, gl, gh, mask_g)
        if mask_b is None or slider is None or slider is bslider:
            bl, bh = bslider.get()
            mask_b = cv2.inRange(image_b, bl, bh, mask_b)

        mask_full = cv2.bitwise_and(mask_r, mask_g, mask_full)
        mask_full = cv2.bitwise_and(mask_full, mask_b, mask_full)

        if masked is not None:
            masked.fill(0)
        masked = cv2.bitwise_and(image, image, masked, mask_full)
        im2.set_image(PIL.Image.fromarray(masked))

    def pixel_pick(x, y):
        r, g, b = image[y, x]
        rslider.set(r - 20, r + 20, False)
        gslider.set(g - 20, g + 20, False)
        bslider.set(b - 20, b + 20, False)
        hex_rgb = "#%02X%02X%02X" % (r, g, b)
        pixel_canvas.config(background=hex_rgb)
        update(None, 0, 0)

    im1.on_pick(pixel_pick)
    for s in (rslider, gslider, bslider):
        s.on_change(update)

    root.mainloop()


if __name__ == "__main__":
    main()
Nahlásit jako SPAM
IP: 213.211.51.–
oxidián0
Grafoman
29. 4. 2024   #14
-
0
-

No ano, o tom přesně přemýšlím. Kdybychom zůstali u matlabu, tak bych počítal s tou možností, že bych spočítal kolik px má ta přiblížená oblast v originálním obraze (tento výpočet jsem už v minulosti dělal). Protože když máš obraz 1024x1024 a zobrazuješ oblast 64x64 tak je fakt zbytečné přepisovat celý originální obraz. Ale už ten první obraz, i kdyby byl v původní velikosti na velkém monitoru třeba cca 1200x1900 , tak by se dalo pomocí mapování udělat předběžné zmenšení na principu LODů, jaké se používají v zobrazovacích mapovacích systémech (google maps, mapy cz, openstreet maps).

takže by bylo třeba vytvořil pole, tuple, či prostě obraz, který ukládá hodnoty

LOD 1: 256x256 = 65536 pixelů
LOD 2: 512x512 = 262144 pixelů
LOD 3: 1024x1024 = 1048576 pixelů
LOD 4: 2048x2048 = 4194304 pixelů
LOD 5: 4096x4096 = 16777216 pixelů
LOD 6: 8192x8192 = 67108864 pixelů
LOD 7: 16384x16384 = 268435456 pixelů
LOD 8: 32768x32768 = 1073741824 pixelů
LOD 9: 65536x65536 = 4294967296 pixelů
LOD 10: 131072x131072 = 17179869184 pixelů
LOD 11: 262144x262144 = 68719476736 pixelů
LOD 12: 524288x524288 = 274877906944 pixelů
LOD 13: 1048576x1048576 = 1099511627776 pixelů
LOD 14: 2097152x2097152 = 4398046511104 pixelů
LOD 15: 4194304x4194304 = 17592186044416 pixelů
LOD 16: 8388608x8388608 = 70368744177664 pixelů

S tím, že asi bych to posunul o dvě úrovně, aby nejmenší hodnota byla 64x64. Pak bych udělal detekční mechanismus, který zjistí jak velký je obrázek, a jak moc je vhodné ho zmenšit. Tedy v praxi jsem se nesetkal s větším obrázkem než co je u LOD8 nebo 9 ale při posunu

Zatím to není otestované, toto napsal chatGPT:

class ImageResizer:
    def __init__(self, image_size):
        self.image_size = image_size
        self.lod_mapping = {
            1: {'dimension': (64, 64), 'resize_power': 0},
            2: {'dimension': (128, 128), 'resize_power': 0},
            3: {'dimension': (256, 256), 'resize_power': 0},
            4: {'dimension': (512, 512), 'resize_power': 0},
            5: {'dimension': (1024, 1024), 'resize_power': 1},
            6: {'dimension': (2048, 2048), 'resize_power': 1},
            7: {'dimension': (4096, 4096), 'resize_power': 2},
            8: {'dimension': (8192, 8192), 'resize_power': 3},
            9: {'dimension': (16384, 16384), 'resize_power': 4},
            10: {'dimension': (32768, 32768), 'resize_power': 5},
            11: {'dimension': (65536, 65536), 'resize_power': 6}
        }

    def calculate_lod(self):
        total_pixels = self.image_size ** 2
        for lod, lod_info in self.lod_mapping.items():
            width, height = lod_info['dimension']
            if total_pixels <= width * height:
                return lod

    def calculate_resized_copy(self, lod):
        lod_info = self.lod_mapping[lod]
        target_width, target_height = lod_info['dimension']
        resize_power = lod_info['resize_power']
        resized_width = self.image_size // (2 ** resize_power)
        resized_height = self.image_size // (2 ** resize_power)
        return resized_width, resized_height

# Example usage:
image_size = 1600  # Example input image size
resizer = ImageResizer(image_size)
lod = resizer.calculate_lod()
resized_width, resized_height = resizer.calculate_resized_copy(lod)
print("LOD:", lod)
print("Resized copy dimensions:", resized_width, "x", resized_height)

Více info konkrétní příklad s hodnotama a výpočtem jak jsem ho popsal ChatGPT:

https://chat.openai.com/share/d03e6ade-5047-461f-99ae-d113f151d80c

Zkontroluju to později.

Respektive implementace toho pole se zmenšeninama:

import cv2
import numpy as np

class ImageResizer:
    def __init__(self, image_size):
        self.image_size = image_size
        self.views = []  # Pole pro ukládání zmenšených kopií obrázku
        self.lod_mapping = {
            1: {'dimension': (64, 64), 'resize_power': 0},
            2: {'dimension': (128, 128), 'resize_power': 0},
            3: {'dimension': (256, 256), 'resize_power': 0},
            4: {'dimension': (512, 512), 'resize_power': 0},
            5: {'dimension': (1024, 1024), 'resize_power': 1},
            6: {'dimension': (2048, 2048), 'resize_power': 1},
            7: {'dimension': (4096, 4096), 'resize_power': 2},
            8: {'dimension': (8192, 8192), 'resize_power': 3},
            9: {'dimension': (16384, 16384), 'resize_power': 4},
            10: {'dimension': (32768, 32768), 'resize_power': 5},
            11: {'dimension': (65536, 65536), 'resize_power': 6}
        }

    def calculate_lod(self):
        total_pixels = self.image_size ** 2
        for lod, lod_info in self.lod_mapping.items():
            width, height = lod_info['dimension']
            if total_pixels <= width * height:
                return lod

    def calculate_resized_copy(self, lod):
        lod_info = self.lod_mapping[lod]
        target_width, target_height = lod_info['dimension']
        resize_power = lod_info['resize_power']
        resized_width = self.image_size // (2 ** resize_power)
        resized_height = self.image_size // (2 ** resize_power)
        return resized_width, resized_height

    def resize_image(self, image):
        lod = self.calculate_lod()
        resized_width, resized_height = self.calculate_resized_copy(lod)
        resized_image = cv2.resize(image, (resized_width, resized_height))
        return resized_image

    def generate_views(self, image):
        for lod in range(1, self.calculate_lod() + 1):
            resized_image = self.resize_image(image)
            self.views.append(resized_image)

# Example usage:
image_size = 1600  # Example input image size
input_image = np.zeros((image_size, image_size), dtype=np.uint8)  # Example input image (black image)
resizer = ImageResizer(image_size)
resizer.generate_views(input_image)
print("Number of views:", len(resizer.views))
Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
29. 4. 2024   #15
-
0
-

Aktuální kód - stále k tomu matlabu - přidal jsem implementaci toho ImageResizer a kontrolu paměti.

NEVYZKOUŠENO, pokračovat budu nejspíš zítra.

import os
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.widgets import RangeSlider
import tkinter as tk
from tkinter import filedialog
from ttkthemes import ThemedTk
import configparser
import psutil

class ImageResizer:
    def __init__(self, image_size):
        self.image_size = image_size
        self.views = []  # Pole pro ukládání zmenšených kopií obrázku
        self.lod_mapping = {
            1: {'dimension': (64, 64), 'resize_power': 0},
            2: {'dimension': (128, 128), 'resize_power': 0},
            3: {'dimension': (256, 256), 'resize_power': 0},
            4: {'dimension': (512, 512), 'resize_power': 0},
            5: {'dimension': (1024, 1024), 'resize_power': 1},
            6: {'dimension': (2048, 2048), 'resize_power': 1},
            7: {'dimension': (4096, 4096), 'resize_power': 2},
            8: {'dimension': (8192, 8192), 'resize_power': 3},
            9: {'dimension': (16384, 16384), 'resize_power': 4},
            10: {'dimension': (32768, 32768), 'resize_power': 5},
            11: {'dimension': (65536, 65536), 'resize_power': 6}
        }

    def calculate_lod(self, bounds):
        bound_width, bound_height = bounds
        for lod, lod_info in self.lod_mapping.items():
            width, height = lod_info['dimension']
            if bound_width <= width and bound_height <= height:
                return lod

    def calculate_memory_required(self, filesize):
        total_memory = filesize
        for lod_info in self.lod_mapping.values():
            width, height = lod_info['dimension']
            lod_memory = width * height * 3  # Assuming 3 channels (RGB)
            total_memory += lod_memory
        return total_memory

    def check_memory(self, required_memory):
        available_memory = psutil.virtual_memory().available
        if available_memory < required_memory:
            print("Not enough memory available to load the image.")
            return False
        elif available_memory < 2 * required_memory:
            print("Warning: Your system has limited memory. The program may not function properly.")
        return True

    def resize_image(self, image):
        lod = self.calculate_lod(image.shape[:2])
        resized_width, resized_height = self.calculate_resized_copy(lod)
        resized_image = cv2.resize(image, (resized_width, resized_height))
        return resized_image

    def generate_views(self, image):
        for lod in range(1, self.calculate_lod(image.shape[:2]) + 1):
            resized_image = self.resize_image(image)
            self.views.append(resized_image)

def save_filename(filename):
    config = configparser.ConfigParser()
    config['DEFAULT'] = {'filename': filename}
    with open('config.cfg', 'w') as configfile:
        config.write(configfile)

def load_filename():
    config = configparser.ConfigParser()
    config.read('config.cfg')
    return config['DEFAULT']['filename']

def load_image():
    try:
        filename = load_filename()
    except KeyError:
        filename = filedialog.askopenfilename()
        save_filename(filename)

    # Check if enough memory is available
    filesize = os.path.getsize(filename)
    resizer = ImageResizer(0)  # Create an instance to access methods
    required_memory = resizer.calculate_memory_required(filesize)
    if not resizer.check_memory(required_memory):
        return None
    
    image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
    return image

def main():
    root = ThemedTk(theme="black")
    root.title("Image Analyzer")

    image = load_image()
    if image is None:
        root.destroy()
        return

    image_r, image_g, image_b = cv2.split(image)

    fig, (ax1, ax2) = plt.subplots(2, 1)  # layout: 2 rows, 1 column
    im1 = ax1.imshow(image)
    im2 = ax2.imshow(image)
    fig.subplots_adjust(bottom=0.30)

    # matplotlib.widgets / RangeSlider
    rslider_ax = fig.add_axes([0.20, 0.20, 0.60, 0.03])
    rslider = RangeSlider(rslider_ax, "R", 0, 255, valinit=(0, 255))
    gslider_ax = fig.add_axes([0.20, 0.15, 0.60, 0.03])
    gslider = RangeSlider(gslider_ax, "G", 0, 255, valinit=(0, 255))
    bslider_ax = fig.add_axes([0.20, 0.1, 0.60, 0.03])
    bslider = RangeSlider(bslider_ax, "B", 0, 255, valinit=(0, 255))

    # Create masks beforehand
    mask_r = cv2.inRange(image_r, 0, 255)
    mask_g = cv2.inRange(image_g, 0, 255)
    mask_b = cv2.inRange(image_b, 0, 255)

    fig_canvas = FigureCanvasTkAgg(fig, master=root)
    fig_canvas.get_tk_widget().pack(expand=True, fill=tk.BOTH)
    fig_toolbar = NavigationToolbar2Tk(fig_canvas, root, pack_toolbar=False)
    fig_toolbar.pack(fill=tk.X)

    def update(val):
        rl, rh = rslider.val
        gl, gh = gslider.val
        bl, bh = bslider.val

        # Update the masks in place
        cv2.inRange(image_r, rl, rh, dst=mask_r)
        cv2.inRange(image_g, gl, gh, dst=mask_g)
        cv2.inRange(image_b, bl, bh, dst=mask_b)

        # Update the combined mask
        cv2.bitwise_and(mask_r, mask_g, mask=mask_b)

        # Update the masked image
        masked = cv2.bitwise_and(image, image, mask=mask_b)
        im2.set_data(masked)
        fig.canvas.draw_idle()

    rslider.on_changed(update)
    gslider.on_changed(update)
    bslider.on_changed(update)

    def pick_color(event):
        if event.inaxes is not ax1:
            return
        x, y = int(event.xdata), int(event.ydata)
        r, g, b = map(float, image[y, x])
        rslider.set_val((r - 20, r + 20))
        gslider.set_val((g - 20, g + 20))
        bslider.set_val((b - 20, b + 20))

    fig_canvas.mpl_connect("button_press_event", pick_color)

    root.mainloop()

if __name__ == "__main__":
    main()

https://chat.openai.com/share/d03e6ade-5047-461f-99ae-d113f151d80c

Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
30. 4. 2024   #16
-
0
-

Zatraceně mě se nedaří. Musel jsem ten tvůj kód předělat do třídy pomocí ChatGPT.

Moc tě prosím, příště požádej ChatGPT aby ten tvůj kód převedl na třídu. Strašně špatně se s tím pracuje jak to není ve třídě, nejde skloubit můj a tvůj kód. ChatGPT s tím nedokáže pracovat.

Tady je "nový produkt", spíš mezi produkt.

import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import RangeSlider
from ttkthemes import ThemedTk
import tkinter as tk
import configparser

class ColorPicker:
    def __init__(self):
        self.fig = None
        self.ax1 = None
        self.ax2 = None
        self.image = None
        self.mask = None
        self.masked_image = None
        self.colorbar = None
        self.rslider = None
        self.gslider = None
        self.bslider = None

    def save_filename(self, filename):
        config = configparser.ConfigParser()
        config['DEFAULT'] = {'filename': filename}
        with open('config.cfg', 'w') as configfile:
            config.write(configfile)

    def load_filename(self):
        config = configparser.ConfigParser()
        config.read('config.cfg')
        return config['DEFAULT']['filename']

    def load_image(self):
        try:
            filename = self.load_filename()
        except KeyError:
            filename = tk.filedialog.askopenfilename()
            self.save_filename(filename)

        print("Loading image:", filename)
        self.image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
        self.mask = np.ones_like(self.image[:, :, 0], dtype=np.uint8) * 255
        self.masked_image = np.copy(self.image)

    def update_mask(self, val):
        print("Updating mask with values:", val)
        rl, rh = self.rslider.val
        gl, gh = self.gslider.val
        bl, bh = self.bslider.val

        # Update the masks in place
        cv2.inRange(self.image, (rl, gl, bl), (rh, gh, bh), dst=self.mask)

        # Update the masked image
        self.masked_image=cv2.bitwise_and(self.image, self.image, mask=self.mask)

        # Update the displayed image
        self.ax2.imshow(self.masked_image)
        self.fig.canvas.draw_idle()

    def pick_color(self, event):
        if event.inaxes is not self.ax1:
            return
        x, y = int(event.xdata), int(event.ydata)
        r, g, b = self.image[y, x]
        print("Picked color at coordinates:", x, y)
        print("RGB values:", r, g, b)
        self.rslider.set_val((r - 20, r + 20))
        self.gslider.set_val((g - 20, g + 20))
        self.bslider.set_val((b - 20, b + 20))

    def main(self):        
        root = ThemedTk(theme="black")
        root.title("Color Picker")

        self.load_image()

        self.fig, (self.ax1, self.ax2) = plt.subplots(1, 2)
        self.ax1.imshow(self.image)
        self.ax2.imshow(self.masked_image)

        self.rslider_ax = self.fig.add_axes([0.20, 0.20, 0.60, 0.03])
        self.rslider = RangeSlider(self.rslider_ax, "R", 0, 255, valinit=(0, 255))
        self.gslider_ax = self.fig.add_axes([0.20, 0.15, 0.60, 0.03])
        self.gslider = RangeSlider(self.gslider_ax, "G", 0, 255, valinit=(0, 255))
        self.bslider_ax = self.fig.add_axes([0.20, 0.1, 0.60, 0.03])
        self.bslider = RangeSlider(self.bslider_ax, "B", 0, 255, valinit=(0, 255))

        self.rslider.on_changed(self.update_mask)
        self.gslider.on_changed(self.update_mask)
        self.bslider.on_changed(self.update_mask)

        self.fig.canvas.mpl_connect("button_press_event", self.pick_color)

        print("Starting mainloop")
        root.mainloop()  # Přidáno pro udržení otevřeného okna

if __name__ == "__main__":
    picker = ColorPicker()
    picker.main()

Toto se mi nepovedlo předělat

self.masked_image=cv2.bitwise_and(self.image, self.image, mask=self.mask)

na

cv2.bitwise_and(self.image, self.image, mask=self.mask, dst=self.masked_image)

nevím proč ale neprovedlo se to maskování...

Teď tam jsou dvě okna, nevím na co je to druhé, to je nějaká chyba.

Připojen obrázek.

Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
30. 4. 2024   #17
-
0
-

Photoshop používá quick masku, jedinou poloprůhlednou barvou překrývá obrázek. Buď červená barva vidět je nebo není. Červená v podstatě odpovídá vybraným pixelům nebo naopak. Úplně nevím jak toto udělat a jestli by to bylo rychlejší než

Připojen obrázek.

než toto:

cv2.inRange(self.image, (rl, gl, bl), (rh, gh, bh), dst=self.mask)
# Update the masked image
self.masked_image=cv2.bitwise_and(self.image, self.image, mask=self.mask)
Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
30. 4. 2024   #18
-
0
-

Udělal jsem nějaké úpravy na tom tvém kódu, přidal jsem tam nějaké funkce, co jsem tam chtěl mít aby tam fungovalo zoomování a dalo se pak počítat s tou třídou pro resize a vytváření zmenšenin náhledů.

Ale už mi to nestartuje.

Dokázal bys prosím tě najít chybu? Ke zbytku jsem se nedostal, chtěl jsem přesunout vytvoření figure a axes do konstruktoru.

Jo, pokusil jsem se implementovat můj starý kód, který počítá s výpoštem hsv hodnot, když kliknu na obrázek a to se řeší přes screenshot a uhnutí kurzoru myši (pyautogui).

import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import RangeSlider
from ttkthemes import ThemedTk
import tkinter as tk
import configparser
import math
import colorsys
import pyautogui

class ColorPicker:
    def __init__(self):
        self.fig = None
        self.ax1 = None
        self.ax2 = None
        self.image = None
        self.mask = None
        self.masked_image = None
        self.colorbar = None
        self.rslider = None
        self.gslider = None
        self.bslider = None
        self.dx = None  # Nově přidaný atribut pro výpočet části původního obrazu
        self.dy = None  # Nově přidaný atribut pro výpočet části původního obrazu
        self.selected_color = None  # Nově přidaný atribut pro uložení vybrané barvy
        self.zoom = None  # Nově přidaný atribut pro zaznamenání stavu zoomu
        self.zoom_finished = None  # Nově přidaný atribut pro zaznamenání stavu dokončení zoomu
        self.zoom_on = False

        # Inicializace Matplotlibu v konstruktoru
#        self.fig, (self.ax1, self.ax2) = plt.subplots(1, 2)

        # Přidání událostního posluchače pro přepínání zoomu
        #tools = self.fig.canvas.toolbar.children
        #tools['!checkbutton2'].bind('<Button-1>', self.zoom_switch)

    def save_filename(self, filename):
        config = configparser.ConfigParser()
        config['DEFAULT'] = {'filename': filename}
        with open('config.cfg', 'w') as configfile:
            config.write(configfile)

    def load_filename(self):
        config = configparser.ConfigParser()
        config.read('config.cfg')
        return config['DEFAULT']['filename']

    def load_image(self):
        try:
            filename = self.load_filename()
        except KeyError:
            filename = tk.filedialog.askopenfilename()
            self.save_filename(filename)

        print("Loading image:", filename)
        self.image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
        self.mask = np.ones_like(self.image[:, :, 0], dtype=np.uint8) * 255
        self.masked_image = np.copy(self.image)

    def update_mask(self, val):
        print("Updating mask with values:", val)
        rl, rh = self.rslider.val
        gl, gh = self.gslider.val
        bl, bh = self.bslider.val

        # Update the masks in place
        cv2.inRange(self.image, (rl, gl, bl), (rh, gh, bh), dst=self.mask)

        # Update the masked image
        self.masked_image=cv2.bitwise_and(self.image, self.image, mask=self.mask)

        # Update the displayed image
        self.ax2.imshow(self.masked_image)
        self.fig.canvas.draw_idle()

    def pick_color(self, event):
        if event.inaxes is not self.ax1:
            return
        x, y = int(event.xdata), int(event.ydata)
        r, g, b = self.image[y, x]
        print("Picked color at coordinates:", x, y)
        print("RGB values:", r, g, b)
        self.rslider.set_val((r - 20, r + 20))
        self.gslider.set_val((g - 20, g + 20))
        self.bslider.set_val((b - 20, b + 20))

    def zoom_switch(self, event):
        if self.zoom_on == False:
            self.zoom_on = True
        else:
            self.zoom_on = False

    def pressed(self, event):
        if self.zoom_finished:
            x, y = pyautogui.position()
            pyautogui.moveRel(0, 1)
            self.selected_color = pyautogui.pixel(x, y)
            pyautogui.moveRel(0, -1)
            print(self.selected_color)
            self.update_color_patch()

            # Aktualizace rozsahu barev colorbaru na základě vybrané barvy

            if self.selected_color:
                r, g, b = self.selected_color[0], self.selected_color[1], self.selected_color[2]
                # Převod RGB na HSL (odstín, sytost, jas)
                h, _, _ = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255)

                # Vypočítání minimálního a maximálního odstínu pro colorbar
                min_hue = max(round((h * 360) - 30), 0)  # Minimální odstín, zaokrouhlený nahoru na 0
                max_hue = min(round((h * 360) + 30), 360)  # Maximální odstín, zaokrouhlený dolů na 360

    def released(self, event):
        if not self.zoom_on:
            return 
        x_min, x_max = plt.xlim()
        y_max, y_min = plt.ylim()
        self.origin = {'x': math.ceil(x_min), 'y': math.ceil(y_min)}
        self.dx = math.ceil(x_max) - self.origin['x']
        self.dy = math.ceil(y_max) - self.origin['y']

    def main(self):        
        root = ThemedTk(theme="black")
        root.title("Color Picker")

        self.load_image()

        self.fig, (self.ax1, self.ax2) = plt.subplots(1, 2)
        self.ax1.imshow(self.image)
        self.ax2.imshow(self.masked_image)
#        tools = self.fig.canvas.toolbar.children
#        tools['!checkbutton2'].bind('<Button-1>', self.zoom_switch)

        self.rslider_ax = self.fig.add_axes([0.20, 0.20, 0.60, 0.03])
        self.rslider = RangeSlider(self.rslider_ax, "R", 0, 255, valinit=(0, 255))
        self.gslider_ax = self.fig.add_axes([0.20, 0.15, 0.60, 0.03])
        self.gslider = RangeSlider(self.gslider_ax, "G", 0, 255, valinit=(0, 255))
        self.bslider_ax = self.fig.add_axes([0.20, 0.1, 0.60, 0.03])
        self.bslider = RangeSlider(self.bslider_ax, "B", 0, 255, valinit=(0, 255))

        self.rslider.on_changed(self.update_mask)
        self.gslider.on_changed(self.update_mask)
        self.bslider.on_changed(self.update_mask)

        self.fig.canvas.mpl_connect("button_press_event", self.pick_color)
        self.fig.canvas.mpl_connect("button_press_event", self.pressed)  # Přidáno pro zachycení stisknutí tlačítka myši
        self.fig.canvas.mpl_connect("button_release_event", self.released)  # Přidáno pro zachycení uvolnění tlačítka myši

        print("Starting mainloop")
        root.mainloop()  # Přidáno pro udržení otevřeného okna

if __name__ == "__main__":
    picker = ColorPicker()
    picker.main()

PS:

Ještě mám nápad. Bylo by možné změnit barvu záhlaví okna nebo okno když kliknu na ten obrázek, aby se změnila barva okna podle aktuálně vybrané barvy? Tím pádem by se nemuselo vytvářet to okno co tam teď najíždí s aktuální barvou co stejně nefunguje.

Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
30. 4. 2024   #19
-
0
-

Už to zase jede. Zatím se mi podařilo roztáhnout canvas na celé okno, ale nepodařilo se mi roztáhnout (zvětšit) ty obrázky ax1 ax2 aby byly větší...

    def main(self):        
        root = ThemedTk(theme="black")
        root.title("Color Picker")

        # Inicializace Matplotlibu v konstruktoru
        self.fig, (self.ax1, self.ax2) = plt.subplots(1, 2, figsize=(8, 6))
        font_size = 8
        font_name = "Arial"
        plt.tick_params(axis='both', which='major', labelsize=font_size, labelfontfamily=font_name)

        # Přidání událostního posluchače pro přepínání zoomu - toto by mělo jít takto přímo
        #tools = self.fig.canvas.toolbar.children
        #tools['!checkbutton2'].bind('<Button-1>', self.zoom_switch)
        self.load_image()

Připojen obrázek.

PS:

Ty reakce jsou dost pomalé i když kliknu na to Zoom nebo na Home. To je divné. A to je malý obrázek 800x600. Asi je to nějaká chyba, to mi dříve nedělalo v té moji první verzi.

Nahlásit jako SPAM
IP: 94.113.179.–
oxidián0
Grafoman
30. 4. 2024   #20
-
0
-

Implementoval jsem základní metody, co tam potřebuju mít, aby šlo kliknout, vybrat barvy.

Teď jsem to naposledy převáděl na hsv režii a připravuju to na práci s těma kopiema náhledů.

Konstruktor je ještě trochu chaotický, chce to lépe roztřídit ty vlastnosti.

import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import RangeSlider
from ttkthemes import ThemedTk
import tkinter as tk
import configparser
import math
import colorsys
import pyautogui

class ColorPicker:
    def __init__(self):
        self.fig = None
        self.ax1 = None
        self.ax2 = None
        self.original_image = None
        self.zoomed_image = None # optimalizovaná verze self.original_image
        self.hsv_zoomed_image_copy = None # hsv version of self.zoomed_image
        self.hsv_image_current = None # soubor na který se má aplikovat maska
        self.mask = None
        self.masked_image = None
        self.colorbar = None
        self.rslider = None
        self.gslider = None
        self.bslider = None
        # dimentions of axis x and axis y during Zoom
        self.zoomed_dx = None  # Nově přidaný atribut pro výpočet části původního obrazu
        self.zoomed_dy = None  # Nově přidaný atribut pro výpočet části původního obrazu
        self.selected_color = None  # Nově přidaný atribut pro uložení vybrané barvy
        self.zoom = None  # Nově přidaný atribut pro zaznamenání stavu zoomu
        self.zoom_finished = None  # Nově přidaný atribut pro zaznamenání stavu dokončení zoomu
        self.zoom_on = False
        self.origin = {'x': 0, 'y': 0}

    def save_filename(self, filename):
        config = configparser.ConfigParser()
        config['DEFAULT'] = {'filename': filename}
        with open('config.cfg', 'w') as configfile:
            config.write(configfile)

    def load_filename(self):
        config = configparser.ConfigParser()
        config.read('config.cfg')
        return config['DEFAULT']['filename']

    def load_image(self):
        try:
            filename = self.load_filename()
        except KeyError:
            filename = tk.filedialog.askopenfilename()
            self.save_filename(filename)

        print("Loading image:", filename)
        self.original_image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
        # @TODO: OPTIMALIZACE: VYTVOŘENÍ ZMENŠENÉHO NÁHLEDU:
        # @TODO: Zde bude optimalizovaná verze obrazu k náhledu pro ax1:
        self.zoomed_image = cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2RGB)
        # @TODO: self.hsv_image bude hsv verze toho optimalizovaného náhledu
        self.hsv_zoomed_image_copy = cv2.cvtColor(self.zoomed_image, cv2.COLOR_RGB2HSV)
        # @TODO: Maska se bude vytvářet pro rozměr té optimalizované verze
        self.mask = np.ones_like(self.zoomed_image[:, :, 0], dtype=np.uint8) * 255
        self.masked_image = np.copy(self.zoomed_image)
        toolbar = plt.get_current_fig_manager().toolbar



    def update_mask(self, val):
        print("Updating mask with values:", val)
        hl, hh = self.rslider.val
        sl, sh = self.gslider.val
        bl, bh = self.bslider.val
        
        # Update the masks in place
        #hsv_image = cv2.cvtColor(self.hsv_image, cv2.COLOR_RGB2HSV)
        # TOTO NENÍ TŘEBA KDYŽ UŽ TEN OBRAZ JE V HSV:
        # cv2.cvtColor(self.hsv_image, cv2.COLOR_RGB2HSV, dst=self.hsv_image)
        cv2.inRange(self.hsv_zoomed_image_copy, (hl, sl, bl), (hh, sh, bh), dst=self.mask)

        # Update the masked image
        # @TODO: TEN SOUBOR MUSÍM NEJPRVE ZKOPÍROVAT ABYCH PRACOVAL S TOU ORIGINÁLNÍ ZMENŠENOU VERZÍ NÁHLADU pro ax2:
        self.hsv_image_current = np.copy(self.hsv_zoomed_image_copy)
        cv2.bitwise_and(self.hsv_zoomed_image_copy, self.mask, mask=self.mask, dst = self.hsv_image_current)

        # Update the displayed image
        self.ax2.imshow(self.masked_image)
        self.fig.canvas.draw_idle()

    def pick_color(self, event):
        if event.inaxes is not self.ax1:
            return
        x, y = int(event.xdata), int(event.ydata)
        r, g, b = self.original_image[y, x]
        print("Picked color at coordinates:", x, y)
        print("RGB values:", r, g, b)

        # Převod RGB hodnot na HSV
        h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255)

        # Nastavení posuvníků na převzaté hodnoty HSV
        self.rslider.set_val((h * 255 - 20, h * 255 + 20))
        self.gslider.set_val((s * 255 - 20, s * 255 + 20))
        self.bslider.set_val((v * 255 - 20, v * 255 + 20))

    def zoom_switch(self, event):
        if self.zoom_on == False:
            self.zoom_on = True
        else:
            self.zoom_on = False

    def set_current_ax_dim(self):
        x_min, x_max = self.ax1.get_xlim()
        y_max, y_min = self.ax1.get_ylim()
        self.origin = {'x': math.ceil(x_min), 'y': math.ceil(y_min)}
        self.zoomed_dx = math.ceil(x_max) - self.origin['x']
        self.zoomed_dy = math.ceil(y_max) - self.origin['y']

    # Aktuálně má událost fungovat jen pro ax1
    def pressed(self, event):
        if event.inaxes is not self.ax1:
            return

        if self.zoom_finished:
            x, y = pyautogui.position()
            pyautogui.moveRel(0, 1)
            self.selected_color = pyautogui.pixel(x, y)
            pyautogui.moveRel(0, -1)
            print(self.selected_color)
            
            # @TODO: Updatovat náhled barvy, implementovat
            # self.update_color_patch()

            # Aktualizace rozsahu barev colorbaru na základě vybrané barvy

        if self.selected_color:
            r, g, b = self.selected_color[0], self.selected_color[1], self.selected_color[2]
            # Převod RGB na HSV (odstín, sytost, jas)
            h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255)

            # Přepočet odstínu do rozsahu 0-255
            h = int(h * 255)
            # Výpočet minimálního a maximálního odstínu pro colorbar
            min_hue = max(h - 30, 0)  # Minimální odstín
            max_hue = min(h + 30, 255)  # Maximální odstín

        # Aktuálně má událost fungovat jen pro ax1
    def released(self, event):
        if event.inaxes is not self.ax1:
            return
        if self.zoom_finished:
            pass
        if not self.zoom_on:
            if not self.zoom_on:
                self.zoom_finished = False
                return 
            return 
        # získá šírku aktuální osy:
        # x_min, x_max = self.ax1.get_xlim()
        # y_min, y_max  = self.ax1.get_ylim()
        # self.origin = {'x': math.ceil(x_min), 'y': math.ceil(y_min)}
        # self.zoomed_dx = math.ceil(x_max) - self.origin['x']
        # self.zoomed_dy = math.ceil(y_max) - self.origin['y']

        self.set_current_ax_dim()        
        self.zoom_finished = True
        self.zoomed_dx = self.zoomed_dx
        self.zoomed_dy = self.zoomed_dx

    def main(self):        
        root = ThemedTk(theme="black")
        root.title("Color Picker")

        # Inicializace Matplotlibu v konstruktoru
        self.fig, (self.ax1, self.ax2) = plt.subplots(1, 2, figsize=(8, 6))
    

        font_size = 8
        font_name = "Arial"
        plt.tick_params(axis='both', which='major', labelsize=font_size, labelfontfamily=font_name)
        m = plt.get_current_fig_manager()        

        self.load_image()
        
        self.ax1.imshow(self.zoomed_image)
        self.ax2.imshow(self.masked_image)        

        tools = self.fig.canvas.toolbar.children
        tools['!checkbutton2'].bind('<Button-1>', self.zoom_switch)

        self.rslider_ax = self.fig.add_axes([0.20, 0.20, 0.60, 0.03])
        self.rslider = RangeSlider(self.rslider_ax, "H", 0, 255, valinit=(0, 255))
        self.gslider_ax = self.fig.add_axes([0.20, 0.15, 0.60, 0.03])
        self.gslider = RangeSlider(self.gslider_ax, "S", 0, 255, valinit=(0, 255))
        self.bslider_ax = self.fig.add_axes([0.20, 0.1, 0.60, 0.03])
        self.bslider = RangeSlider(self.bslider_ax, "V", 0, 255, valinit=(0, 255))

        self.rslider.on_changed(self.update_mask)
        self.gslider.on_changed(self.update_mask)
        self.bslider.on_changed(self.update_mask)

        # self.fig.canvas.mpl_connect("button_press_event", self.pick_color)
        self.fig.canvas.mpl_connect("button_press_event", self.pressed)  # Přidáno pro zachycení stisknutí tlačítka myši
        self.fig.canvas.mpl_connect("button_release_event", self.released)  # Přidáno pro zachycení uvolnění tlačítka myši

        print("Starting mainloop")
        root.mainloop()  # Přidáno pro udržení otevřeného okna

if __name__ == "__main__":
    picker = ColorPicker()
    picker.main()

pick_color jsem odstavil, nemohou současně běžet obě funkce (pressed).

# self.fig.canvas.mpl_connect("button_press_event", self.pick_color)
        self.fig.canvas.mpl_connect("button_press_event", self.pressed)  # Přidáno pro zachycení stisknutí tlačítka myši

Při načítání obrázku se vytváří zmenšený RGB náhled (self.zoomed_image) a kopie tohoto náhledu je konvertována do HSV prostoru (self.hsv_zoomed_image_copy). To zaručuje, že pracuju s vhodnou verzí pro zobrazení v ax1 a s vhodnou verzí pro práci s barvami v ax2.

Metoda update_mask nyní používá self.hsv_zoomed_image_copy pro vytvoření masky v HSV prostoru. Dále se vytvoří kopie masky pro aplikaci na původní obrázek.

Funkce cv2.bitwise_and je použita k aplikaci masky na původní obrázek v HSV prostoru (self.hsv_zoomed_image_copy). Výstup je uložen do self.hsv_image_current, což je vhodná verze obrázku pro zobrazení v ax2. (V této části jsem měl největší nejistotu jestli to vůbec dělám správně)

Metoda pick_color používá self.original_image pro získání hodnoty barev pro vybraný pixel, což je správně, protože uživatel interaguje s původním obrázkem.

Je to celé neotestované.

Nahlásit jako SPAM
IP: 94.113.179.–
Zjistit počet nových příspěvků

Přidej příspěvek

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 12 hostů

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý