Jak obliczyć gradient kolorów

Jak obliczyć gradient kolorów
CD
CD
  • Rejestracja:ponad 4 lata
  • Ostatnio:prawie 2 lata
  • Postów:10
1

Dzień dobry,

próbuje napisać skrypt w pythonie który narysuje zadany gradient.
Pisząc skrypt natrafiłem na trudności związane z tym że moim językiem macierzystym jest C++, w nim zrobiłem mapkę wysokości i kilka innych zdań.
Niestety w tym zadaniu jest wymóg aby zrobić skrypt w Pythonie.
Z samym Pythonem radzę sobie mimo trudności jednak mam problem natury merytorycznej.

Nie ummiem wymyślić wzoru, formuły którą pobierjąc wartość v z zakresu od 0.0 do 1.0 wyilicząła by jaki należy teraz podać kolor pixela do rysowania tak aby uszyskać żadany gradient Siedziałem długo na color pickerami, patrzyłem różne modele barw, próbowałem wplatać w funkcje wyliczająca kolory r,b,g pixela funkcje matematyczne typu sinus a teraz nawet próbowałem robić to na masie instrukcji warunkowych. Oczywiście wszystko zawiodło, dodatkowym problemem jest to że w C++ stosuje obiektowe podejście do programowania a tutaj wymuszono na mnie podejście funkcyjne - sprawia mi to dodatkowa trudność bo nie jestem przyzwyczajony do tego sposobu myślenia o kodzie.

Jakiej pomocy oczekuje ale wezmę wszystko:
Chciałbym abyście naprowadzili mnie na wzór matematyczny który pozwoli uzyskać taki gradient jaki jest na obrazku w załączniku.
Jeśli w systemie RGB jest taki wzór problemem to napisałem w c++ funkcje która przelicza HSL czy HSV do systemu RGB, mogę ją zaimplementować w Python.

Cel - jak ma wyglądać drugi gradient od góry:
gradient cel.png

Obecny "efekt"
eket.png

Kod źródłowy:

Kopiuj
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import division             
import matplotlib
import math
import os
matplotlib.use('Agg')                      
import matplotlib.pyplot as plt
from matplotlib import rc
import numpy as np

from matplotlib import colors
krok = 0
def plot_color_gradients(gradients, names):

    rc('legend', fontsize=10)

    column_width_pt = 400        
    pt_per_inch = 72
    size = column_width_pt / pt_per_inch

    fig, axes = plt.subplots(nrows=len(gradients), sharex=True, figsize=(size, 0.75 * size))
    fig.subplots_adjust(top=1.00, bottom=0.05, left=0.25, right=0.95)


    for ax, gradient, name in zip(axes, gradients, names):
       
        img = np.zeros((2, 1024, 3))
        for i, v in enumerate(np.linspace(0, 1, 1024)):
            img[:, i] = gradient(v)

        im = ax.imshow(img, aspect='auto')
        im.set_extent([0, 1, 0, 1])
        ax.yaxis.set_visible(False)

        pos = list(ax.get_position().bounds)
        x_text = pos[0] - 0.25
        y_text = pos[1] + pos[3]/2.
        fig.text(x_text, y_text, name, va='center', ha='left', fontsize=10)

    fig.savefig('my-gradients.pdf')

def hsv2rgb(h, s, v):
    #
    return (h, s, v)

def gradient_rgb_bw(v):
    #gotowe
    return (v,v ,v)

r = 0
g = 255
b = 0
def gradient_rgb_gbr(v):
    
    global r
    global g 
    global b 

    g = g - 0.5
    if(g <= 0):g=0

    if(v >0.2 and r==0):
        b = b+ 0.5

    if( b > 255):b=255

    if (v > 0.55):r = r +0.5 #to niest na ifach juz z desperacji:(
    print(r,g,b)

    


    return (  int(r),  int(g),  int(b) )

def gradient_rgb_gbr_full(v):
    
    return (v, v, 0)


def gradient_rgb_wb_custom(v):
    
    return (0, 0, 0)


def gradient_hsv_bw(v):
    
    return hsv2rgb(0, 0, 0)


def gradient_hsv_gbr(v):
    
    return hsv2rgb(0, 0, 0)

def gradient_hsv_unknown(v):
    
    return hsv2rgb(0, 0, 0)


def gradient_hsv_custom(v):
    
    return hsv2rgb(0, 0, 0)


if __name__ == '__main__':
    def toname(g):
        return g.__name__.replace('gradient_', '').replace('_', '-').upper()

    gradients = (gradient_rgb_bw, gradient_rgb_gbr, gradient_rgb_gbr_full, gradient_rgb_wb_custom,
                 gradient_hsv_bw, gradient_hsv_gbr, gradient_hsv_unknown, gradient_hsv_custom)

    plot_color_gradients(gradients, [toname(g) for g in gradients])

Pozdrawiam

edytowany 1x, ostatnio: Riddle
Patryk27
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:prawie 2 lata
  • Lokalizacja:Wrocław
  • Postów:13042
1

Z tego co widzę, żadnych sinusów ani sztuczek tutaj nie potrzeba - taki gradient to zwyczajna liniowa interpolacja (osobno dla każdego kanału) :-)

Czyli gradient z jednego koloru na drugi to będzie coś w stylu (pisane z palca):

Kopiuj
fn lerp(Color from, Color to, float progress) -> Color
  return Color {
      r: lerp(from.r, to.r, progress),
      g: lerp(from.g, to.g, progress),
      b: lerp(from.b, to.b, progress),
  }

fn lerp(float from, float to, float progress) -> float
  return from + (to - from) * progress

Podobnie będzie to wyglądało dla n kolorów.

Btw, z tymi zmiennymi globalnymi to tak średnio funkcyjne Twoje podejście :-P


edytowany 11x, ostatnio: Patryk27
Spine
  • Rejestracja:około 22 lata
  • Ostatnio:22 minuty
  • Postów:6694
1
Patryk27 napisał(a):
Kopiuj
fn lerp(float from, float to, float progress) -> float
  return min(from, to) + abs(from - to) * progress

lerp(1.0, 0.0, 0.3) == lerp(0.0, 1.0, 0.3), a nie powinno ;)
Bo jak idziemy od 1.0 do 0.0 o 0.3, to mamy 0.7.
A jak idziemy od 0.0 do 1.0 o 0.3, to mamy 0.3.


🕹️⌨️🖥️🖱️🎮
edytowany 1x, ostatnio: Spine
Patryk27
whoopsie! chyba teraz będzie dobrze :-)
CD
CD
  • Rejestracja:ponad 4 lata
  • Ostatnio:prawie 2 lata
  • Postów:10
0

Dziękuje za odpowiedzi.

Zaimplementowałem funkcje @Patryk27

Kopiuj
def lerp(_from, _to,  _progress):
  return _from + (_to - _from) * _progress

Opracowałem wcześniej sposób "interpolacji" opartej o dekrementacje :) ale twoja funkcja jest bardziej elegancka. Biere :P

Jak widać na obrazku z załącznika funkcja działa, wartości kanałów r,g,b zmniejszają się by w końcu osiągnąć zadana wartość.
Mimo to python cały blok maluje na biało.

Zawartość funkcji :

Kopiuj
def gradient_rgb_gbr(v):
    

    r = lerp(255,128,v)
    g= lerp(255,128,v)
    b= lerp(255,128,v)
    
    print(r,g,b)

    return (int(r),int(g),int(b))

Wypisałem że funkcja zwraca w tym momencie odcienie szarości. Nie rozumiem dlaczego biblioteka rysująca koloruje pasek na biało.

Załóżmy że powyższy problem rozwiąże(my). Jestem w stanie uzyskać odcienie różnych kolorów (powyżej uzyskałem przejście z białego w czarne abstrahując od tego że nie rysuje mi python tego gradnietu).

W tym zadaniu będę musiał od początku do ok 1/3 boxa rysować odcienie zielonego, potem od 1/3 do 2/3 boxa odcienie niebieskiego i od 2/3 do końca odcienie czerwonego.
Moja funkcja przyjmuje parametr v (zakres wartości 0.f do 1.f) i na tej podstawie muszę wiedzieć czy zwracać odcień zielonego, niebieskiego czy czerwonego.

Mam pomysł aby zrobić to za pomocą instrukcji warunkowych i zaraz wypróbuje ten pomysł ale czy tak jest profesjonalnie?

#EDIT

Kopiuj
dodałem astype('uint8')

do

Kopiuj
im = ax.imshow(img, aspect='auto')

także teraz jest

Kopiuj
im = ax.imshow(img.astype('uint8'), aspect='auto')
edytowany 1x, ostatnio: CiscoDisco
CD
CD
  • Rejestracja:ponad 4 lata
  • Ostatnio:prawie 2 lata
  • Postów:10
0
Kopiuj
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import division             
import matplotlib
import math
import os
matplotlib.use('Agg')                      
import matplotlib.pyplot as plt
from matplotlib import rc
import numpy as np

from matplotlib import colors
krok = 0
def plot_color_gradients(gradients, names):

    rc('legend', fontsize=10)

    column_width_pt = 400        
    pt_per_inch = 72
    size = column_width_pt / pt_per_inch

    fig, axes = plt.subplots(nrows=len(gradients), sharex=True, figsize=(size, 0.75 * size))
    fig.subplots_adjust(top=1.00, bottom=0.05, left=0.25, right=0.95)


    for ax, gradient, name in zip(axes, gradients, names):
       
        img = np.zeros((2, 1024, 3))
        for i, v in enumerate(np.linspace(0, 1, 1024)):
            img[:, i] = gradient(v)

        im = ax.imshow(img.astype('uint8'), aspect='auto')
        #plt.imshow(out.astype('uint8'))
        im.set_extent([0, 1, 0, 1])
        ax.yaxis.set_visible(False)

        pos = list(ax.get_position().bounds)
        x_text = pos[0] - 0.25
        y_text = pos[1] + pos[3]/2.
        fig.text(x_text, y_text, name, va='center', ha='left', fontsize=10)

    fig.savefig('my-gradients.pdf')

def hsv2rgb(h, s, v):
    #
    return (h, s, v)

def gradient_rgb_bw(v):
    #gotowe
    return (v,v ,v)

def gradient_rgb_gbr(v):
    
    r = 0
    g = 0
    b = 0

    if(v >0 and v < 0.3):
        r = lerp(0,130,v*4)
        g= lerp(255,255,0)
        b= lerp(0,130,v*4)

    if(v >=0.3 and v < 0.5):
        r = lerp(200,0,(v-0.3)*4)
        g= lerp(200,0,(v-0.3)*4)
        b= lerp(255,255,1)

    if(v >=0.5 and v < 0.7):
        r = lerp(0,200,(v-0.5)*4)
        g= lerp(0,200,(v-0.5)*4)
        b= lerp(255,255,1)
    if(v >=0.7):
        r = lerp(255,255,1)
        g= lerp(200,0,(v-0.7)*3)
        b= lerp(200,0,(v-0.7)*3)

    

    return (int(r),int(g),int(b))

def gradient_rgb_gbr_full(v):
    
    return (v, v, 0)


def gradient_rgb_wb_custom(v):
    
    return (0, 0, 0)


def gradient_hsv_bw(v):
    
    return hsv2rgb(0, 0, 0)


def gradient_hsv_gbr(v):
    
    return hsv2rgb(0, 0, 0)

def gradient_hsv_unknown(v):
    
    return hsv2rgb(0, 0, 0)


def gradient_hsv_custom(v):
    
    return hsv2rgb(0, 0, 0)

def lerp(_from, _to,  _progress):
  return _from + (_to - _from) * _progress


if __name__ == '__main__':
    def toname(g):
        return g.__name__.replace('gradient_', '').replace('_', '-').upper()

    gradients = (gradient_rgb_bw, gradient_rgb_gbr, gradient_rgb_gbr_full, gradient_rgb_wb_custom,
                 gradient_hsv_bw, gradient_hsv_gbr, gradient_hsv_unknown, gradient_hsv_custom)

    plot_color_gradients(gradients, [toname(g) for g in gradients])

@Patryk27: @Spine

Uzyskałem mniej więcej porzadany efekt, Chciałbym zapytać o funkcje def gradient_rgb_gbr(v): czy w Pythonie można by ją zapisać jakoś lepiej. Pythona dopero się ucze więc nie znam wszystkich jego "technik" czy instrukcji. Jeśli można proszę o podpowiedź.

Pozdrawiam.

Patryk27
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:prawie 2 lata
  • Lokalizacja:Wrocław
  • Postów:13042
1

Na Pythonie znam się so-so, ale generalnie poleciłbym odejście od ręcznie hard-kodowanych ifów na rzecz bardziej abstrakcyjnego designu - w stylu (w pseudokodzie):

Kopiuj
struct Color
  int r
  int g
  int b

fn gradient(f32 progress, Color[] colors)
  int idx = lerp(0, colors.len(), progress).roundDown()
  
  Color from = colors[idx]
  Color to = colors[idx + 1]

  ...

/* ... i potem: */

for ...:
  ...
  
  for i, v in enumerate(np.linspace(0, 1, 1024)):
    ...
    
    gradient(progress, [
      Color { ... },
      Color { ... },
      Color { ... },
    ])

Spine
  • Rejestracja:około 22 lata
  • Ostatnio:22 minuty
  • Postów:6694
0

Możesz porównywać v inaczej:

Kopiuj
>>> v = 0.4
>>> 0 < v < 0.3
False
>>> 0.3 <= v < 0.5
True

🕹️⌨️🖥️🖱️🎮

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.