2016-09-14 22 views
5

To jest mniej więcej kolejne pytanie do Two dimensional color ramp (256x256 matrix) interpolated from 4 corner colors, na które odpowiedź została dziś głęboko odebrana przez jadsq.Elementy interpolujące macierzy kolorów na podstawie niektórych podanych elementów odniesienia.

Dla gradientów liniowych poprzednia odpowiedź działa bardzo dobrze. Jeśli jednak chcemy mieć lepszą kontrolę nad gradientem kolorów gradientu, metoda ta wydaje się mało praktyczna. W tej sytuacji pomocne mogą być punkty odniesienia w macierzy (tabela odnośników), które służą do interpolacji wartości kolorów dla pustej pozycji w tabeli przeglądowej. Co mam na myśli może być łatwiejsze odczytanie z poniższego obrazu.

enter image description here

Cała idea jest pobierana z http://cartography.oregonstate.edu/pdf/2006_JennyHurni_SwissStyleShading.pdf strony 4 do 6. Czytałem przez papier, rozumiem, teoretycznie, co się dzieje, ale w przypadku braku zdało egzaminu z powodu mojego niskiego doświadczenia z metod interpolacji i być uczciwe, ogólne umiejętności matematyczne. Co może być interesujące, to że używa sigmoidalnego dzwonka Gaussa jako metody interpolacji (strona 6). Twierdzą, że ważenie Gaussa przyniosło najlepsze wizualnie wyniki i było proste do obliczenia (równanie 1, przy k = 0,0002 dla tabeli 256 na 256 komórek).


Edit (lepsze ilustracje):

Weighting functions for interpolating colours

Equation 1


mam inne części ich prezentowanych metod w miejscu, ale wypełnianie pustych wartości w macierzy naprawdę jest kluczową częścią i powstrzymuje mnie od kontynuowania. Jeszcze raz dziękuję za pomoc!

Co mam teraz:

#!/usr/bin/env python3 
import numpy as np 
import matplotlib.pyplot as plt 

# the matrix with the reference color elements 
ref=np.full([7, 7, 3], [255,255,255], dtype=np.uint8) 
ref[0][6] = (239,238,185) 
ref[1][1] = (120,131,125) 
ref[4][6] = (184,191,171) 
ref[6][2] = (150,168,158) 
ref[6][5] = (166,180,166) 

# s = ref.shape 
# 
# from scipy.ndimage.interpolation import zoom 
# zooming as in https://stackoverflow.com/a/39485650/1230358 doesn't seem to work here anymore, because we have no corner point as reference but randomly distributed points within the matrix. As far as I know ... 
# zoomed=zoom(ref,(256/s[0],256/s[1],1),order=1) 

plt.subplot(211) 
plt.imshow(ref,interpolation='nearest') 
# plt.subplot(212) 
# plt.imshow(zoomed,interpolation='nearest') 
plt.show() 

Odpowiedz

5

Pierwsze kilka pytań, aby lepiej wyjaśnić swoje wątpliwości:

  • jaką chcesz interpolacji: liniowa/sześcienny/inny?
  • Jakie są ograniczenia punktów? na przykład czy zawsze będzie tylko jeden region zamknięty przez te punkty kontrolne, czy też mogą być w nim punkty?

Dla prostego interpolacji liniowej i arbitralne (ale co najmniej 3 punkty nie na jednej linii) chciałbym spróbować to: obszar punkty kontrolne

  1. triangulacji

    do nie nakładania się trójkąty obejmujące cały określony obszar.

  2. renderowania trójkątów

    Więc po prostu rasterize zobaczyć Algorithm to fill triangle i wszystkie sublinks. Powinieneś interpolować również R,G,B wraz ze współrzędnymi.

  3. Tworzenie do 2 kopii gradientu i przenieść jedną z H i druga z linii V

    Tak przemiatania wszystkich H poziomych linii gradientu, a w przypadku stwierdzenia 2 Znany pikseli wystarczająco daleko od siebie (na przykład ćwiartka lub połowa wielkości gradientu), a następnie ekstrapoluj całą linię nieznanych kolorów. Więc jeśli znaleziono znane punkty końcowe (Red) są (x0,y,r0,g0,b0),(x1,y,r1,g1,b1) następnie ustawić wszystkie nieznane kolory w tej samej linii co:

    r = r0+(r1-r0)*(x-x0)/(x1-x0) 
    g = g0+(g1-g0)*(x-x0)/(x1-x0) 
    b = b0+(b1-b0)*(x-x0)/(x1-x0) 
    

    Podobnie zrobić to samo w kopii gradientu dla linii pionowej V-teraz. Więc punkty są teraz (x, y0, R0, G0, B0), (x, y1, R1, g1, b1) `i ekstrapolacja:

    r = r0+(r1-r0)*(y-y0)/(y1-y0) 
    g = g0+(g1-g0)*(y-y0)/(y1-y0) 
    b = b0+(b1-b0)*(y-y0)/(y1-y0) 
    

    Po to porównać obie kopie i jeśli nieznany punkt jest obliczany w obu ustawić go jako średnią obu kolorów w obrazie gradientu docelowego. Zapętlaj cały ten proces (# 3), dopóki nie dodasz nowego piksela gradientowego.

  4. stosowanie pojedynczego ekstrapolowanych kolor dla odpoczynku

    zależności od sposobu zdefiniowania punktów kontrolnych niektóre obszary będą miały tylko 1 ekstrapolować kolor (albo z H lub linii V, ale nie oba), więc używać tylko pojedynczy obliczony kolor dla tych (po wykonaniu # 3).

Oto przykład tego, co rozumiem przez to wszystko:

overview

Jeśli chcesz coś prostego zamiast (ale nie dokładnie), to możesz krwawić znanych punktów kontrolnych kolory (z gładką filtry) do sąsiednich pikseli, dopóki cały gradient nie zostanie wypełniony i nasycony.

  1. wypełnienia nieznany gradientu pikseli z predefiniowanych kolorów rozumieniu nie obliczane
  2. ustawić każdy piksel średnich obliczonych swoich sąsiadów

    Można to zrobić w osobnym obrazek, aby uniknąć przesunięcia.

    punktów
  3. zespół sterowania z powrotem do pierwotnego koloru

  4. pętli 2 aż obszar wypełniony/nasycony/lub ustalonej liczby iteracji

[Edit1] Drugie rozwiązanie

Ok Położyłem to razem w C++ ze swoich punktów/kolory i wielkości gradientu Oto jak to wygląda (I krwawić 100 razy 4-sąsiedzi krwawienia bez obciążników):

bleeding

Obraz po lewej stronie jest wejściowy macierz gdzie zakodowany do alfa kanał (najwyższe 8 bitów), jeśli piksel jest punktem odniesienia, obliczony lub jeszcze nieokreślony. Obraz po prawej stronie jest po 100-krotnym nałożeniu krwawienia. Upuszczanie jest proste, po prostu weź dowolny punkt odniesienia i przelicz go jako średnią wszystkich użytecznych pikseli wokół i siebie (ignorując nieokreślone kolory).

Oto kod C++ można zignorować GDI materiał do renderowania (uwaga moja Mapa gradientu ma x koordynować pierwszy masz y!)

//--------------------------------------------------------------------------- 
const int mxs=7,mys=7,msz=16; // gradient resolution x,y and square size for render 
DWORD map[mxs][mys];   // gradient matrix ... undefined color is >= 0xFF000000 
// 0x00?????? - reference color 
// 0xFF?????? - uncomputed color 
// 0xFE?????? - bleeded color 
//--------------------------------------------------------------------------- 
void map_clear() // set all pixels as uncomputed (white with alpha=255) 
    { 
    int x,y; 
    for (x=0;x<mxs;x++) 
    for (y=0;y<mys;y++) 
     map[x][y]=0xFFFFFFFF; 
    } 
void map_bleed() // bleed computed colors 
    { 
    int x,y,r,g,b,n; 
    DWORD tmp[mxs][mys],c; 
    for (x=0;x<mxs;x++) 
    for (y=0;y<mys;y++) 
     { 
     c=map[x][y]; 
     n=0; r=0; g=0; b=0; if (DWORD(c&0xFF000000)==0) { tmp[x][y]=c; continue; }  if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; } 
     x++;  if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; } 
     x--; y--; if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; } 
     x--; y++; if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; } 
     x++; y++; if ((x>=0)&&(x<mxs)&&(y>=0)&&(y<mys)) c=map[x][y]; else c=0xFF000000; if (DWORD(c&0xFF000000)!=0xFF000000) { r+=c&255; g+=(c>>8)&255; b+=(c>>16)&255; n++; } 
     y--;  if (!n) { tmp[x][y]=0xFFFFFFFF; continue; } 
     c=((r/n)|((g/n)<<8)|((b/n)<<16))&0x00FFFFFF; 
     tmp[x][y]=c; 
     } 
    // copy tmp back to map 
    for (x=0;x<mxs;x++) 
    for (y=0;y<mys;y++) 
     map[x][y]=tmp[x][y]; 
    } 
void map_draw(TCanvas *can,int x0,int y0) // just renders actual gradient map onto canvas (can ignore this) 
    { 
    int x,y,xx,yy; 
    for (x=0,xx=x0;x<mxs;x++,xx+=msz) 
    for (y=0,yy=y0;y<mys;y++,yy+=msz) 
     { 
     can->Pen->Color=clBlack; 
     can->Brush->Color=map[x][y]&0x00FFFFFF; 
     can->Rectangle(xx,yy,xx+msz,yy+msz); 
     } 
    } 
//--------------------------------------------------------------------------- 

A tu wykorzystanie (Twój przykład):

// clear backbuffer 
bmp->Canvas->Brush->Color=clBlack; 
bmp->Canvas->FillRect(TRect(0,0,xs,ys)); 

// init your gradient with reference points 
map_clear(); 
// x y  R  G  B 
map[6][0] = (239)|(238<<8)|(185<<16); 
map[1][1] = (120)|(131<<8)|(125<<16); 
map[6][4] = (184)|(191<<8)|(171<<16); 
map[2][6] = (150)|(168<<8)|(158<<16); 
map[5][6] = (166)|(180<<8)|(166<<16); 
map_draw(bmp->Canvas,msz,msz); // render result (left) 
// bleed 
for (int i=0;i<100;i++) map_bleed(); 
map_draw(bmp->Canvas,(mxs+2)*msz,msz); // render result (right) 

// refresh window with backbufer (anti-flickering) 
Main->Canvas->Draw(0,0,bmp); 

Ponownie można zignorować wszystkie renderowane elementy. Liczba spadów powinna być 2x większa niż piksele w przekątnej, więc krwawienie pokrywa wszystkie piksele. Im więcej powtórzeń tym bardziej nasycone wynik staram 100 tylko dla przykładu i wynik wygląda dobrze .. więc nie grałem z nim już ...

[Edit2] i tu algorytm dla drugiego podejścia

  1. dodać flagi interpolowanej matrycy

    trzeba wiedzieć, jeśli piksel jest reference,undefined lub interpolated. Możesz zakodować to na kanale alfa lub użyć maski (osobnej matrycy 2D).

  2. upustowy/gładka matrycy

    zasadzie dla każdego piksela nie reference obliczyć nową wartość jako średnią z wszystkich nie undefined pikseli wokół sąsiadów (4/8) i na jej miejsce. Nie używaj pikseli undefined i przechowuj wyliczoną wartość na tymczasowej matrycy (nie zmyślaj kolejnych pikseli, w przeciwnym razie efekt krwawienia/wygładzania przesuwałby piksele zazwyczaj po przekątnej). W ten sposób niezdefiniowane obszary pikseli zmniejszą się o 1 piksel. Po wykonaniu całej macierzy należy skopiować zawartość macierzy tymczasowej do oryginalnej macierzy (lub zamienić wskaźniki).

  3. pętli 2 aż wyniku jest nasycony lub specyficzna liczba iteracji

    ilość zliczeń powinna wynosić co leas 2 razy większy niż liczba pikseli ukośnych propagacji piksel odniesienia na całej matrycy. Kontrola nasycenia może być wykonana w # 2 podczas kopiowania tablicy temp do oryginalnej (może zrobić różnicę abs między klatkami i jeśli zero lub w pobliżu zatrzyma się).

+0

Dziękuję @Spektre za poświęcenie swojego czasu na to dokładnej odpowiedzi! Nigdy nie pomyślałbym o triangulacji w tym celu. Z pewnością spróbuję. Chociaż muszę przyznać, że spodziewałem się (z powodu mojej zwykłej ignorancji) łatwiejszego rozwiązania dla macierzy i niestrukturalnych danych, w jakiś sposób bazując na '' 'griddata'''' 'BivariateSpline''' (http://stackoverflow.com/a/5147409/1230358). Muszę przemyśleć i eksperymentować z Twoimi sugestiami. W każdym razie dziękuję za poświęcony czas i pomoc !!! Z przyjemnością przyjmuję twoją odpowiedź, jeśli nikt nie wymyśli łatwiejszego rozwiązania ... – hetsch

+0

... które może być oparte na numpy lub scipy i macierzach. http://stackoverflow.com/q/5146025/1230358 dał mi kilka wskazówek, ale wciąż walczę, aby to zadziałało. – hetsch

+0

Dodałem lepsze ilustracje papieru, z którego mam oryginalny pomysł. Z tego co rozumiem, pracują z odległością od punktu odniesienia do punktu, który ma być kolorowy. Wydaje się być podobnym do pierwszego rozwiązania, które zasugerowałeś. – hetsch

1

Jestem tu znowu (trochę późno, przepraszam, ja tylko znalazłem pytanie) o dość krótkim roztworu stosując griddata z scipy.interpolate. Ta funkcja ma dokładnie to, czego potrzebujesz: interpolować wartości na siatce z zaledwie kilku punktów. Kwestie są następujące: z tym, że nie będziesz w stanie używać fantazyjnych wag tylko predefiniowaną metodę interpolacji, a dziury wokół granicy nie mogą być bezpośrednio interpolowane, więc tutaj wypełniłem je najbliższymi wartościami.

Oto kod demo:

# the matrix with the reference color elements 
ref=np.full([7, 7, 3], 0 , dtype=np.uint8) 
#Note I fill with 0 instead of 255 
ref[0][6] = (239,238,185) 
ref[1][1] = (120,131,125) 
ref[4][6] = (184,191,171) 
ref[6][2] = (150,168,158) 
ref[6][5] = (166,180,166) 

from scipy.interpolate import griddata 

#we format the data to feed in griddata 
points=np.where(ref != 0) 
values=ref[points] 
grid_x,grid_y,grid_z=np.mgrid[0:7,0:7,0:3] 

#we compute the inperpolation 
filled_grid=griddata(points, values, (grid_x, grid_y, grid_z), method='linear') 
filled_grid=np.array(filled_grid,dtype=np.uint8) #we convert the float64 to uint8 

#filled_grid still has holes around the border 
#here i'll complete the holes with the nearest value 
points=np.where(filled_grid != 0) 
values=filled_grid[points] 
near_grid=griddata(points, values, (grid_x, grid_y, grid_z), method='nearest') 
completed_grid=(near_grid*(filled_grid == 0))+filled_grid 

plt.subplot(131) 
plt.imshow(ref,interpolation='nearest') 
plt.subplot(132) 
plt.imshow(filled_grid,interpolation='nearest') 
plt.subplot(133) 
plt.imshow(completed_grid,interpolation='nearest') 
plt.show() 

wyjściowa: output of the code