WedX - журнал о программировании и компьютерных науках

Выравнивание и обрезка одинаковых изображений сцены

Здравствуйте, у меня есть разные изображения, сделанные с использованием брекетинга экспозиции (одна и та же сцена с разной экспозицией), мне нужно выровнять изображения и обрезать каждое из них, чтобы они точно совпадали. (поскольку при съемке этих изображений было дрожание камеры)

Я не хочу их объединять, я просто хочу вырезать, вращать или масштабировать и т. д. каждый из них, чтобы они были точно выровнены, а затем сохранить их. Я бы добавил пример кода, если бы знал, как это сделать. но я понятия не имею. Я новичок в opencv.

Вот пример:

введите здесь описание изображения

Вот реальный пример сэмпла: (у этого сэмпла огромное смещение, большинству сэмплов нужны лишь небольшие корректировки из-за тряски, в отличие от этого)

введите здесь описание изображения

Что мне нужно, так это обрезать каждое из изображений, чтобы сделать их идентичными (оставить только общую область)

Благодарю вас !


  • Репрезентативны ли ваши изображения? Выравнивание простых черных фигур на белом фоне — это не то же самое, что выравнивание реальных фотографий... 21.06.2020
  • @MarkSetchell спасибо за быстрый ответ, я обновил вопрос и добавил дополнительную информацию. 21.06.2020
  • Это для задачи кодирования или для результата? Бесплатная программа, такая как Hugin, может это сделать (Feature matching: align image stack). Существует даже скрипт align_image_stack для автоматизации этой функции. Если вы хотите закодировать это, имейте в виду, что ваша реальная проблема заключается в том, что ось объектива изменилась, поэтому проекция внешнего мира на ваш датчик не такая же, и это сложнее, чем масштабирование/поворот/сдвиг . И надо учитывать дисторсию объектива... 22.06.2020
  • @xenoid это не задача кодирования. я пытаюсь научить нейронную сеть выполнять слияние экспозиции (mericam.github.io/exposure_fusion /index.html) входными данными должны быть разные экспозиции (3 изображения) одной и той же сцены. и вывод (1 изображение) должен быть более подробным, лучше представлять сцену 23.06.2020

Ответы:


1

В этом ответе я описываю подход к достижению выравнивания изображения, который заключается в использовании евклидовой модели для преобразования изображения 1 (слева) и < em>изображение 3 (справа) в соответствии с изображением 2, центральным изображением.

введите здесь описание изображения

введите здесь описание изображения

Тем не менее, я хотел бы быстро указать, что изображения, которыми вы делитесь, очень сложны: не только большая разница в контрасте, но и значительная разница в переводе, масштабе. , поворот и, возможно, даже немного сдвига. Тот факт, что они были опубликованы с очень низким разрешением, также не помогает.

Во всяком случае, я указал на их различия с точки зрения 2D-преобразований, потому что об этом всегда нужно помнить при выборе подходящей модели для выполнения выравнивания изображения. Это красивое изображение взято из руководства, в котором модели описываются более подробно. :

Подход состоит из следующих шагов:

  • Используйте алгоритм расширенного коэффициента корреляции (ECC) для выравнивания изображений с использованием евклидовой модели между изображениями 2 и 1;
  • Затем возвращенная матрица преобразования используется для преобразования изображения 1 с помощью cv2.warpAffine() и вычисления приблизительной прямоугольной области преобразования в изображении 1;
  • Повторите те же шаги для преобразования изображения 3: используйте алгоритм расширенного коэффициента корреляции (ECC) для выполнения выравнивания изображения с использованием евклидовой модели между изображениями 2 и 3;
  • Затем возвращенная матрица преобразования используется для преобразования изображения 3 с помощью cv2.warpAffine() и вычисления приблизительной прямоугольной области преобразования.
  • Результатом этих операций являются выровненные изображения, которые можно увидеть на изображении ниже. Зеленые прямоугольники показывают область трансформации:

введите здесь описание изображения

  • Красный прямоугольник в центральном изображении, эталонное изображение, используемое для создания модели для преобразований, представляет собой пересечение между областями на изображениях 1 и 3 и может рассматриваться как общая область между всеми 3 изображениями.

  • Затем красный прямоугольник можно использовать для обрезки изображений 1, 2 и 3 и придания красивого вида выравниванию изображения. Обратите внимание, как местность и небо на всех этих изображениях кажутся идеально выровненными друг с другом:

введите здесь описание изображения

Интересно отметить стоимость этого подхода: поскольку изображение 1 не зафиксировало все те особенности местности, которые можно легко увидеть на изображениях 2 и 3, конечный результат состоит в том, что изображения 2 и 3 в конечном итоге теряют эту часть местности. Таким образом, все изображения показывают одну и ту же область фотографии.

Исходный код Python:

###
# reference:
#   https://www.learnopencv.com/image-alignment-ecc-in-opencv-c-python/
###
import numpy as np
import cv2

# internalRect: returns the intersection between two rectangles
#
#  p1 ---------------- p2
#   |                  |
#   |                  |
#   |                  |
#  p4 ---------------- p3
def internalRect(r1, r2):
    x = 0
    y = 1
    w = 2
    h = 3

    rect1_pt1 = [       r1[x], r1[y]       ]
    rect1_pt2 = [ r1[x]+r1[w], r1[y]       ]
    rect1_pt3 = [ r1[x]+r1[w], r1[y]+r1[h] ]
    rect1_pt4 = [       r1[x], r1[y]+r1[h] ]

    rect2_pt1 = [       r2[x], r2[y]       ]
    rect2_pt2 = [ r2[x]+r2[w], r2[y]       ]
    rect2_pt3 = [ r2[x]+r2[w], r2[y]+r2[h] ]
    rect2_pt4 = [       r2[x], r2[y]+r2[h] ]

    int_pt1   = [ max(rect1_pt1[x], rect2_pt1[x]), max(rect1_pt1[y], rect2_pt1[y]) ]
    int_pt2   = [ min(rect1_pt2[x], rect2_pt2[x]), max(rect1_pt2[y], rect2_pt2[y]) ]
    int_pt3   = [ min(rect1_pt3[x], rect2_pt3[x]), min(rect1_pt3[y], rect2_pt3[y]) ]
    int_pt4   = [ max(rect1_pt4[x], rect2_pt4[x]), min(rect1_pt4[y], rect2_pt4[y]) ]

    rect =  [ int_pt1[x], int_pt1[y], int_pt2[x]-int_pt1[x], int_pt4[y]-int_pt1[y] ]
    return rect


# align_image: use src1 as the reference image to transform src2
def align_image(src1, src2, warp_mode=cv2.MOTION_TRANSLATION):
    # convert images to grayscale
    img1_gray = cv2.cvtColor(src1, cv2.COLOR_BGR2GRAY)
    img2_gray = cv2.cvtColor(src2, cv2.COLOR_BGR2GRAY)

    # define 2x3 or 3x3 matrices and initialize it to a identity matrix
    if warp_mode == cv2.MOTION_HOMOGRAPHY:
        warp_matrix = np.eye(3, 3, dtype=np.float32)
    else:
        warp_matrix = np.eye(2, 3, dtype=np.float32)

    # number of iterations:
    num_iters = 1000

    # specify the threshold of the increment in the correlation coefficient between two iterations
    termination_eps = 1e-8    

    # Define termination criteria
    criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, num_iters,  termination_eps)

    print('findTransformECC() may take a while...')

    # perform ECC: use the selected model to calculate the transformation required to align src2 with src1. The resulting transformation matrix is stored in warp_matrix:
    (cc, warp_matrix) = cv2.findTransformECC(img1_gray, img2_gray, warp_matrix, warp_mode, criteria, inputMask=None, gaussFiltSize=1)

    if (warp_mode == cv2.MOTION_HOMOGRAPHY):
        img2_aligned = cv2.warpPerspective(src2, warp_matrix, (src1.shape[1], src1.shape[0]), flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP)
    else :
        # use warpAffine() for: translation, euclidean and affine models
        img2_aligned = cv2.warpAffine(src2, warp_matrix, (src1.shape[1], src1.shape[0]), flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP, borderMode=cv2.BORDER_CONSTANT, borderValue=0)

    #print('warp_matrix shape', warp_matrix.shape, 'data=\n', warp_matrix)
    #print(warp_matrix, warp_matrix)

    # compute the cropping area to remove the black bars from the transformed image
    x = 0
    y = 0
    w = src1.shape[1]
    h = src1.shape[0]

    if (warp_matrix[0][2] < 0):
        x = warp_matrix[0][2] * -1
        w -= x

    if (warp_matrix[1][2] < 0):
        y = warp_matrix[1][2] * -1
        h -= y

    if (warp_matrix[1][2] > 0):
        h -= warp_matrix[1][2]
    
    matchArea = [ int(x), int(y), int(w), int(h) ]
  
    #print('src1 w=', src1.shape[1], 'h=', src1.shape[0])
    #print('matchedRect=', matchArea[0], ',', matchArea[1], '@', matchArea[2], 'x', matchArea[3], '\n')
    return img2_aligned, matchArea


##########################################################################################


img1 = cv2.imread("img1.png")
img2 = cv2.imread("img2.png")
img3 = cv2.imread("img3.png")

# TODO: adjust contrast on all input images

###
# resize images to be the same size as the smallest image for debug purposes
###
max_h = img1.shape[0]
max_h = max(max_h, img2.shape[0])
max_h = max(max_h, img3.shape[0])
max_w = img1.shape[1]
max_w = max(max_w, img2.shape[1])
max_w = max(max_w, img3.shape[1])
img1_padded = cv2.resize(img1, (max_w, max_h), interpolation=cv2.INTER_AREA)
img2_padded = cv2.resize(img2, (max_w, max_h), interpolation=cv2.INTER_AREA)
img3_padded = cv2.resize(img3, (max_w, max_h), interpolation=cv2.INTER_AREA)

# stack them horizontally for display
hStack = np.hstack((img1_padded, img2_padded))  # stack images side-by-side
input_stacked = np.hstack((hStack, img3_padded))      # stack images side-by-side
cv2.imwrite("input_stacked.jpg", input_stacked)
cv2.imshow("input_stacked", input_stacked)
cv2.waitKey(0)

###
# perform image alignment
###

# specify the motion model
warp_mode = cv2.MOTION_EUCLIDEAN   # cv2.MOTION_TRANSLATION, cv2.MOTION_EUCLIDEAN, cv2.MOTION_AFFINE, cv2.MOTION_HOMOGRAPHY

# for testing purposes: img2 will be the reference image
img1_aligned, matchArea1 = align_image(img2, img1, warp_mode)
img1_aligned_cpy = img1_aligned.copy()
cv2.rectangle(img1_aligned_cpy, (matchArea1[0], matchArea1[1]),  (matchArea1[0]+matchArea1[2], matchArea1[1]+matchArea1[3]), (0, 255, 0), 2)
cv2.imwrite("img1_aligned.jpg", img1_aligned_cpy)

print('\n###############################################\n')

# for testing purposes: img2 will be the reference image again
img3_aligned, matchArea3 = align_image(img2, img3, warp_mode)
img3_aligned_cpy = img3_aligned.copy()
cv2.rectangle(img3_aligned_cpy, (matchArea3[0], matchArea3[1]),  (matchArea3[0]+matchArea3[2], matchArea3[1]+matchArea3[3]), (0, 255, 0), 2)
cv2.imwrite("img3_aligned.jpg", img3_aligned_cpy)

# compute the crop area in the reference image and draw a red rectangle
cropRect = internalRect(matchArea1, matchArea3)
print('cropRect=', cropRect[0], ',', cropRect[1], '@', cropRect[2], 'x', cropRect[3], '\n')

img2_eq_cpy = img2.copy()
cv2.rectangle(img2_eq_cpy, (cropRect[0], cropRect[1]),  (cropRect[0]+cropRect[2], cropRect[1]+cropRect[3]), (0, 0, 255), 2)
cv2.imwrite("img2_eq.jpg", img2_eq_cpy)

# stack results horizontally for display
res_hStack = np.hstack((img1_aligned_cpy, img2_eq_cpy))                 # stack images side-by-side
aligned_stacked = np.hstack((res_hStack, img3_aligned_cpy))             # stack images side-by-side
cv2.imwrite("aligned_stacked.jpg", aligned_stacked)
cv2.imshow("aligned_stacked", aligned_stacked)
cv2.waitKey(0)

print('\n###############################################\n')

# crop images to the smallest internal area between them
img1_aligned_cropped = img1_aligned[cropRect[1] : cropRect[1]+cropRect[3], cropRect[0] : cropRect[0]+cropRect[2]]
img3_aligned_cropped = img3_aligned[cropRect[1] : cropRect[1]+cropRect[3], cropRect[0] : cropRect[0]+cropRect[2]]
img2_eq_cropped      =         img2[cropRect[1] : cropRect[1]+cropRect[3], cropRect[0] : cropRect[0]+cropRect[2]]

cropped_hStack = np.hstack((img1_aligned_cropped, img2_eq_cropped))     # stack images side-by-side
cropped_stacked = np.hstack((cropped_hStack, img3_aligned_cropped))     # stack images side-by-side
cv2.imwrite("cropped_stacked.jpg", cropped_stacked)
cv2.imshow("cropped_stacked", cropped_stacked)
cv2.waitKey(0)
29.06.2020
Новые материалы

Как проанализировать работу вашего классификатора?
Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

Работа с цепями Маркова, часть 4 (Машинное обучение)
Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

Учебные заметки: создание моего первого пакета Node.js
Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

ИИ в аэрокосмической отрасли
Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


Для любых предложений по сайту: [email protected]