2011-03-02 11 views
7

Mam mapę wysokości obrazu, która informuje mnie o przesunięciu każdego piksela w kierunku Z. Moim celem jest spłaszczenie zniekształconego obrazu przy użyciu tylko jego mapy wysokości.Zniekształcenie obrazu przy użyciu mapy wysokości?

Jak miałbym to zrobić? Znam położenie kamery, jeśli to pomaga.


Aby to zrobić, myślałem o założeniu, że każdy piksel był punkt na płaszczyźnie, a następnie przetłumaczyć każdy z tych punktów w pionie według Z. wartości otrzymuję z mapy wysokości, a od zrobienia to tłumaczenie (wyobraź sobie, że patrzysz na punkty z góry, przesunięcie spowoduje, że punkt porusza się z twojej perspektywy).

Z tej rzutowanej zmiany mogłem wyodrębnić X i przesunięcie Y każdego piksela, które mogłem podać do cv.Remap().

Ale nie mam pojęcia, w jaki sposób mogę uzyskać rzutowane przesunięcie 3D punktu za pomocą OpenCV, nie mówiąc już o budowie mapy odsunięcia.


Oto moje obrazy odniesienia do tego, co robię:

Calibration Image Warped Image

wiem kąt laserów (45 stopni), a ze zdjęć kalibracji, mogę obliczyć wysokość książki naprawdę łatwo:

h(x) = sin(theta) * abs(calibration(x) - actual(x)) 

Robię to dla obu linii i liniowo interpolacji dwóch lin es do generowania powierzchni przy użyciu tego podejścia (kod Pythona. To wewnątrz pętli):

height_grid[x][y] = heights_top[x] * (cv.GetSize(image)[1] - y) + heights_bottom[x] * y 

Mam nadzieję, że to pomoże;)


Teraz, to co muszę dewarp obraz. Wszystko, co dziwne rzeczy w środku projektów współrzędnych 3D na płaszczyznę aparatu, biorąc pod uwagę to stanowisko (i położenie aparatu, obracanie, itp):

class Point: 
    def __init__(self, x = 0, y = 0, z = 0): 
    self.x = x 
    self.y = y 
    self.z = z 

mapX = cv.CreateMat(cv.GetSize(image)[1], cv.GetSize(image)[0], cv.CV_32FC1) 
mapY = cv.CreateMat(cv.GetSize(image)[1], cv.GetSize(image)[0], cv.CV_32FC1) 

c = Point(CAMERA_POSITION[0], CAMERA_POSITION[1], CAMERA_POSITION[2]) 
theta = Point(CAMERA_ROTATION[0], CAMERA_ROTATION[1], CAMERA_ROTATION[2]) 
d = Point() 
e = Point(0, 0, CAMERA_POSITION[2] + SENSOR_OFFSET) 

costx = cos(theta.x) 
costy = cos(theta.y) 
costz = cos(theta.z) 

sintx = sin(theta.x) 
sinty = sin(theta.y) 
sintz = sin(theta.z) 


for x in xrange(cv.GetSize(image)[0]): 
    for y in xrange(cv.GetSize(image)[1]): 

    a = Point(x, y, heights_top[x/2] * (cv.GetSize(image)[1] - y) + heights_bottom[x/2] * y) 
    b = Point() 

    d.x = costy * (sintz * (a.y - c.y) + costz * (a.x - c.x)) - sinty * (a.z - c.z) 
    d.y = sintx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) + costx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 
    d.z = costx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) - sintx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 

    mapX[y, x] = x + (d.x - e.x) * (e.z/d.z) 
    mapY[y, x] = y + (d.y - e.y) * (e.z/d.z) 


print 
print 'Remapping original image using map...' 

remapped = cv.CreateImage(cv.GetSize(image), 8, 3) 
cv.Remap(image, remapped, mapX, mapY, cv.CV_INTER_LINEAR) 

ta zamienia się w ogromną wątku obrazów i kodu teraz ... W każdym razie, ten fragment kodu zajmuje moje 7 minut, aby uruchomić na obrazie z kamery 18MP; to zbyt długo, a na koniec to podejście nie robi nic dla obrazu (przesunięcie dla każdego piksela to << 1).

Wszelkie pomysły?

Odpowiedz

3

skończyło się na wdrażaniu własne rozwiązanie:

for x in xrange(cv.GetSize(image)[0]): 
    for y in xrange(cv.GetSize(image)[1]): 

    a = Point(x, y, heights_top[x/2] * (cv.GetSize(image)[1] - y) + heights_bottom[x/2] * y) 
    b = Point() 

    d.x = costy * (sintz * (a.y - c.y) + costz * (a.x - c.x)) - sinty * (a.z - c.z) 
    d.y = sintx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) + costx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 
    d.z = costx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) - sintx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 

    mapX[y, x] = x + 100.0 * (d.x - e.x) * (e.z/d.z) 
    mapY[y, x] = y + 100.0 * (d.y - e.y) * (e.z/d.z) 


print 
print 'Remapping original image using map...' 

remapped = cv.CreateImage(cv.GetSize(image), 8, 3) 
cv.Remap(image, remapped, mapX, mapY, cv.CV_INTER_LINEAR) 

To (powoli) remaps każdy piksel przy użyciu funkcji cv.Remap, a to wydaje się rodzajem pracy ...

0

Zniekształcenie oparte na odległości od kamery występuje tylko przy projekcji perspektywicznej. Jeśli masz pozycję (x, y, z) piksela, możesz użyć macierzy projekcyjnej kamery, aby zrenderować piksele z powrotem do przestrzeni świata. Dzięki tym informacjom możesz renderować piksele w sposób ortogonalny. Jednak możesz mieć brakujące dane ze względu na oryginalną projekcję perspektywy.

+0

Can OpenCV map 3D do 2D ? Czy muszę wymyślić własną formułę? Spróbuję to jednak wdrożyć, dzięki! – Blender

0

oddzielić scena w następujący sposób:

  • masz nieznaną bitmapę I (x, y) -> (r, g, b)
  • masz znanego pola wysokość H (x, y) -> h
  • masz aparat przekształcić C (x, y, z) -> (u, v), która wystaje scenę do płaszczyzny ekranu

Należy pamiętać, że przekształcenie kamery powoduje odrzucenie informacji (nie otrzymuje się wartości głębokości dla każdego piksela ekranu). Na ekranie mogą również pojawiać się fragmenty sceny, w którym to przypadku wyświetlana jest tylko strona główna - reszta jest odrzucana. Zasadniczo nie jest to całkowicie odwracalne.

  • trzeba ekranu S (u, v), co jest wynikiem C (x, y, H (x, y)) dla X, Y, w I
  • aby wygenerować zrzut S (U ', V'), które jest wynikiem C (x, 0) dla x, Y, w I

Istnieją dwa oczywiste sposoby podejścia do tego; oba zależą od posiadania dokładnych wartości dla transformacji kamery.

  1. Ray-odlew: dla każdego piksela w S, rzucić ray z powrotem na scenę. Dowiedz się, gdzie trafia na wysokość; to daje (x, y) na obrazie oryginalnym I, a piksel ekranu daje kolor w tym miejscu. Po odzyskaniu jak największej ilości danych, ponownie je przekształć, aby znaleźć S '.

  2. Podwójne renderowanie: dla każdego x, y w I, projekt do znalezienia (u, v) i (u ', v'). Wybierz kolor piksela z S (u, v) i skopiuj go do S ' (u', v ').

Obie metody będą mieć problemy z pobieraniem próbek, które mogą być wspomagane przez superpróbkowanie lub interpolację; Metoda 1 pozostawia puste przestrzenie w zatopionych obszarach obrazu, metoda 2 "przepuszcza" od pierwszej powierzchni.

Edit:

ja domniemywać masz na myśli heightfield CG stylu, gdzie każdy piksel w S jest bezpośrednio nad odpowiednim miejscu w S '; ale nie jest tak, jak strona przesuwa się po powierzchni. Strona jest przymocowana do kręgosłupa i nie jest rozciągliwa - uniesienie środka strony pociągnie wolną krawędź w kierunku kręgosłupa.

Na podstawie przykładowego obrazu konieczne będzie odwrócenie tego skumulowanego pociągnięcia - wykrycie położenia i orientacji linii środkowej grzbietu oraz praca stopniowo w lewo i w prawo, znajdowanie zmiany wysokości na górze i na dole każdego pionowego paska strony , obliczając wynikowe zwężenie i przekrzywienie, i odwracając je, aby ponownie utworzyć oryginalną płaską stronę.

+0

Odpowiednio zredagowałem swoją odpowiedź. Dodam również obrazy referencyjne, żebyś mógł zobaczyć, co mam na myśli. – Blender

+0

Tak, przykładowe obrazy są bardzo pomocne. Kilka myśli: po pierwsze, możesz sprawić, że obraz będzie prawie ortogonalny, zaczynając od obiektywu teleobiektywowego i strzelając z jak najdalej do tyłu. Po drugie, strony są spoczynkowe z pewnym pionowym pochyleniem do nich - spoczynkowa dolna krawędź względem płaskiej powierzchni może zmniejszyć lub wyeliminować to. Następnie korekcja obrazu kończy się właśnie korektą szerokości przez arccosine kąta padania strony (czyli naprawdę proste). –

+0

Utknąłem z obiektywem z 3-krotnym zoomem, więc będę musiał żyć z ręczną korekcją stycznych i radialnych wypaczeń. Czy mógłbyś rozwinąć nieco więcej na temat metody 'arccos()'? Nie całkiem to rozumiem. – Blender

Powiązane problemy