Сшивание изображения - PullRequest
3 голосов
/ 06 мая 2019

Я записал видео во время вращения бутылки. Затем я получил кадры из видео и вырезал центральный блок из всех изображений.

enter image description here

Итак, для всех кадров я получил следующие изображения:
enter image description here

Я пытался сшить их, чтобы получить панораму, но у меня плохие результаты. Я использовал следующую программу:

import glob

#rom panorama import Panorama
import sys
import numpy
import imutils
import cv2


def readImages(imageString):
    images = []

    # Get images from arguments.
    for i in range(0, len(imageString)):
        img = cv2.imread(imageString[i])
        images.append(img)

    return images


def findAndDescribeFeatures(image):
    # Getting gray image
    grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Find and describe the features.
    # Fast: sift = cv2.xfeatures2d.SURF_create()
    sift = cv2.xfeatures2d.SIFT_create()

    # Find interest points.
    keypoints = sift.detect(grayImage, None)

    # Computing features.
    keypoints, features = sift.compute(grayImage, keypoints)

    # Converting keypoints to numbers.
    keypoints = numpy.float32([kp.pt for kp in keypoints])

    return keypoints, features


def matchFeatures(featuresA, featuresB):
    # Slow: featureMatcher = cv2.DescriptorMatcher_create("BruteForce")
    featureMatcher = cv2.DescriptorMatcher_create("FlannBased")
    matches = featureMatcher.knnMatch(featuresA, featuresB, k=2)
    return matches


def generateHomography(allMatches, keypointsA, keypointsB, ratio, ransacRep):
    if not allMatches:
        return None
    matches = []

    for match in allMatches:
        # Lowe's ratio test
        if len(match) == 2 and (match[0].distance / match[1].distance) < ratio:
            matches.append(match[0])

    pointsA = numpy.float32([keypointsA[m.queryIdx] for m in matches])
    pointsB = numpy.float32([keypointsB[m.trainIdx] for m in matches])

    if len(pointsA) > 4:
        H, status = cv2.findHomography(pointsA, pointsB, cv2.RANSAC, ransacRep)
        return matches, H, status
    else:
        return None


paths = glob.glob("C:/Users/andre/Desktop/Panorama-master/frames/*.jpg")
images = readImages(paths[::-1])

while len(images) > 1:
    imgR = images.pop()
    imgL = images.pop()

    interestsR, featuresR = findAndDescribeFeatures(imgR)
    interestsL, featuresL = findAndDescribeFeatures(imgL)
    try:
        try:
            allMatches = matchFeatures(featuresR, featuresL)
            _, H, _ = generateHomography(allMatches, interestsR, interestsL, 0.75, 4.0)

            result = cv2.warpPerspective(imgR, H,
                                     (imgR.shape[1] + imgL.shape[1], imgR.shape[0]))
            result[0:imgL.shape[0], 0:imgL.shape[1]] = imgL
            images.append(result)
        except TypeError:
            pass
    except cv2.error:
        pass
result = imutils.resize(images[0], height=260)
cv2.imshow("Result", result)
cv2.imwrite("Result.jpg", result)

cv2.waitKey(0)

Мой результат был:
enter image description here

Может быть, кто-то знает, что делать лучше? Я думаю, что использование небольших блоков из рамки должно удалить округлость ... Но ...

Данные: https://1drv.ms/f/s!ArcAdXhy6TxPho0FLKxyRCL-808Y9g

1 Ответ

1 голос
/ 06 мая 2019

Мне удалось добиться хорошего результата. Я немного переписал твой код, вот измененная часть:

def generateTransformation(allMatches, keypointsA, keypointsB, ratio):
    if not allMatches:
        return None
    matches = []

    for match in allMatches:
        # Lowe's ratio test
        if len(match) == 2 and (match[0].distance / match[1].distance) < ratio:
            matches.append(match[0])

    pointsA = numpy.float32([keypointsA[m.queryIdx] for m in matches])
    pointsB = numpy.float32([keypointsB[m.trainIdx] for m in matches])

    if len(pointsA) > 2:
        transformation = cv2.estimateRigidTransform(pointsA, pointsB, True)
        if transformation is None or transformation.shape[1] < 1 or transformation.shape[0] < 1:
            return None
        return transformation
    else:
        return None


paths = glob.glob("a*.jpg")
images = readImages(paths[::-1])
result = images[0]

while len(images) > 1:
    imgR = images.pop()
    imgL = images.pop()

    interestsR, featuresR = findAndDescribeFeatures(imgR)
    interestsL, featuresL = findAndDescribeFeatures(imgL)
    allMatches = matchFeatures(featuresR, featuresL)

    transformation = generateTransformation(allMatches, interestsR, interestsL, 0.75)
    if transformation is None or transformation[0, 2] < 0:
        images.append(imgR)
        continue
    transformation[0, 0] = 1
    transformation[1, 1] = 1
    transformation[0, 1] = 0
    transformation[1, 0] = 0
    transformation[1, 2] = 0
    result = cv2.warpAffine(imgR, transformation, (imgR.shape[1] + 
                int(transformation[0, 2] + 1), imgR.shape[0]))
    result[:, :imgL.shape[1]] = imgL
    cv2.imshow("R", result)
    images.append(result)
    cv2.waitKey(1)

cv2.imshow("Result", result)

Итак, ключевая вещь, которую я изменил, - это трансформация изображений. Я использую estimateRigidTransform() вместо findHomography() для вычисления преобразования изображения. Из этой матрицы преобразования я извлекаю только перевод координат x , который находится в ячейке [0, 2] полученной матрицы Affine Transformation transformation. Я установил другие элементы матрицы преобразования, как если бы это было преобразование идентичности (без масштабирования, без перспективы, без поворота или перевода y ). Затем я передаю его на warpAffine() для преобразования imgR так же, как вы сделали с warpPerspective().

Вы можете сделать это, потому что у вас есть стабильные положения камеры и вращающегося объекта, и вы снимаете с прямым видом объекта спереди. Это означает, что вам не нужно вносить какие-либо поправки в перспективное / масштабирующее / поворотное изображение и вы можете просто «склеить» их вместе по оси x .

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

enter image description here

Распознавание совпадающих точек изображения также может быть немного неточным, поэтому извлечение только перевода x еще лучше, потому что вы даете алгоритму «подсказку», какая у вас реальная ситуация. Это делает его менее применимым для других условий, но в вашем случае это значительно улучшает результат.

Также я отфильтровываю некоторые неправильные результаты с проверкой if transformation[0, 2] < 0 (она может вращаться только в одном направлении, и код не будет работать, если он в любом случае отрицателен).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...