Гомография для преобразования изображения работает неправильно

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

1) Сопоставление шаблонов с несколькими масштабами

import cv2 as cv2
import numpy as np
import imutils


def main():
    template1 = cv2.imread("C:\\Users\\Manthika\\Desktop\\opencvtest\\templates\\template1.jpg")
    template2 = cv2.imread("C:\\Users\\Manthika\\Desktop\\opencvtest\\templates\\temp.jpg")
    templates = [template1, template2]

    for i in range(len(templates)):
        templates[i] = cv2.cvtColor(templates[i], cv2.COLOR_BGR2GRAY)
        templates[i] = cv2.Canny(templates[i], 50, 140)
        templates[i] = cv2.GaussianBlur(templates[i],(5,5),0)
        templates[i] = imutils.resize(templates[i], width=50)

    (tH, tW) = templates[0].shape[:2]
    # print(tH)
    # print(tW)
    # cv2.imshow("Template", template)

    cap = cv2.VideoCapture(0)

    if cap.isOpened():
        ret, frame = cap.read()
    else:
        ret = False

    # loop over the frames to find the template
    while ret:
        # load the image, convert it to grayscale, and initialize the
        # bookkeeping variable to keep track of the matched region
        ret, frame = cap.read()
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        found = None

        # loop over the scales of the image
        for scale in np.linspace(0.2, 1.0, 20)[::-1]:
            # resize the image according to the scale, and keep track
            # of the ratio of the resizing
            resized = imutils.resize(gray, width=int(gray.shape[1] * scale))
            r = gray.shape[1] / float(resized.shape[1])

            # if the resized image is smaller than the template, then break
            # from the loop
            if resized.shape[0] < tH or resized.shape[1] < tW:
                print("frame is smaller than the template")
                break

            # detect edges in the resized, grayscale image and apply template
            # matching to find the template in the image
            edged = cv2.Canny(resized, 50, 160)
            blurred = cv2.GaussianBlur(edged,(5,5),0)

            curr_max = 0
            index = 0
            result = None

            # find the best match
            for i in range(len(templates)):
                # perform matchtemplate
                res = cv2.matchTemplate(blurred, templates[i], cv2.TM_CCOEFF)
                # get the highest correlation value of the result
                maxVal = res.max()
                # if the correlation is highest thus far, store the value and index of template
                if maxVal > curr_max:
                    curr_max = maxVal
                    index = i
                    result = res

            print(index)
            # result = cv2.matchTemplate(edged, templates[index], cv2.TM_CCOEFF)
            (_, maxVal, _, maxLoc) = cv2.minMaxLoc(result)

            # if we have found a new maximum correlation value, then update
            # the bookkeeping variable
            if found is None or maxVal > found[0]:
                found = (maxVal, maxLoc, r)

            # unpack the bookkeeping variable and compute the (x, y) coordinates
            # of the bounding box based on the resized ratio
        # print(found)
        (_, maxLoc, r) = found
        (startX, startY) = (int(maxLoc[0] * r), int(maxLoc[1] * r))
        (endX, endY) = (int((maxLoc[0] + tW) * r), int((maxLoc[1] + tH) * r))

Это отлично работает и, как я и ожидал, плюс никаких ошибок. Я могу получить значения (startX, startY) и (endX, endY), чтобы нарисовать ограничивающую рамку вокруг обнаруженной области.

2) Вставьте png в обнаруженную область, используя альфа-смешивание

        cropped = frame[startY:endY, startX:endX]
        cv2.imshow("cropped", cropped)

        # Read the foreground image with alpha channel
        foreGroundImage = cv2.imread("C:\\Users\\Manthika\\Desktop\\opencvtest\\tattoo2.png", -1)
        # Read background image
        background = cropped
        dim = (background.shape[1], background.shape[0])
        foreGroundImage = cv2.resize(foreGroundImage, dim)

        # Split png foreground image
        b, g, r, a = cv2.split(foreGroundImage)

        # Save the foregroung RGB content into a single object
        foreground = cv2.merge((b, g, r))

        # Save the alpha information into a single Mat
        alpha = cv2.merge((a, a, a))

        # background = cv2.resize(background, dim, interpolation = cv2.INTER_AREA)

        # Convert uint8 to float
        foreground = foreground.astype(float)
        background = background.astype(float)
        alpha = alpha.astype(float) / 255

        # Perform alpha blending
        foreground = cv2.multiply(alpha, foreground)
        beta = 1.0 - alpha
        background = cv2.multiply(beta, background)
        outImage = cv2.add(foreground, background)
        outImage = outImage/255
        cv2.imshow("outImage", outImage)
        print(outImage.shape)

Здесь я обрезал обнаруженную часть кадра и вставил в нее png. outImage является результатом этого процесса. И я получаю это, как и ожидал.

3) Использование гомографии для преобразования изображения

        # Read source image.
        im_src = outImage.copy()
        size = im_src.shape

        # Create a vector of source points.
        pts_src = np.array(
            [
                [0, 0],
                [size[1] - 1, 0],
                [size[1] - 1, size[0] - 1],
                [0, size[0] - 1]
            ], dtype=float
        )

        # Read destination image
        im_dst = frame.copy()
        cv2.imshow("im_dst", im_dst)

        # Create a vector of destination points.
        pts_dst = np.array(
            [
                [startX, startY],
                [endX, startY],
                [endX, endY],
                [startX, endY]
            ]
        )

        # Calculate Homography between source and destination points
        h, status = cv2.findHomography(pts_src, pts_dst)

        # Warp source image
        im_temp = cv2.warpPerspective(im_src, h, (im_dst.shape[1], im_dst.shape[0]))

        # Black out polygonal area in destination image.
        cv2.fillConvexPoly(im_dst, pts_dst.astype(int), 0, 16)

        # Add warped source image to destination image.
        im_dst = im_dst + im_temp


        cv2.imshow("Final", im_dst)
        cv2.imshow("frame2222", frame)


        if cv2.waitKey(1) == 27:
            break

    cv2.destroyAllWindows()
    cap.release()


if __name__ == "__main__":
    main()

Здесь я хочу, чтобы альфа-смешанный outImage был вставлен в заданные точки кадра. Он отлично работает, когда я заменяю im_src = outImage.copy() с im_src = cv2.imread("someimage.png") и запускаю. Я могу прочитать изображение и вставить его во фрейм, но не могу взять outImage и сделать то же самое. Было бы здорово, если бы вы могли помочь мне с этим. пожалуйста, дайте мне знать, если вам нужно изображение, которое я использовал, или выходы.

ИЗМЕНИТЬ:

Вывод с отображением im_src = cv2.imread("someimage.png") someimage.png в шаблоне output1

Вывод с im_src = outImage.copy() другой частью кадра белого цвета output2


person thadaBoy    schedule 03.05.2019    source источник
comment
Что ты не можешь? выдает ошибку? это ничего не делает с изображением? это неправильно? можете привести пример результирующего изображения для плохого и для хорошего случая?   -  person api55    schedule 03.05.2019
comment
Конечно, @api55 я обновил вопрос. Спасибо.   -  person thadaBoy    schedule 03.05.2019
comment
можете ли вы напечатать im_src.shape и im_src.dtype для обоих случаев (хорошего и плохого)?   -  person api55    schedule 03.05.2019
comment
@api55 Да, конечно. Плохой результат — (120, 101, 3), dtype = float64, а хороший — (704, 1000, 3) dtype = unit8. Спасибо   -  person thadaBoy    schedule 03.05.2019
comment
@api55 Вы уже дали ответ! Большое вам спасибо :))) Я потратил часы, чтобы решить эту проблему. Спасибо еще раз:)   -  person thadaBoy    schedule 03.05.2019
comment
Да, dtype был ответом, я написал полное объяснение в ответе :)   -  person api55    schedule 03.05.2019


Ответы (1)


Проблема

В плохом случае, то есть с im_src = outImage.copy(), у вас есть dtype= float64, и если вы посмотрите на эту строку:

outImage = outImage/255

Затем вы заметите, что ваши значения идут от 0 до 1. Тогда у вас есть:

im_dst = frame.copy()

а также

im_temp = cv2.warpPerspective(im_src, h, (im_dst.shape[1], im_dst.shape[0]))
...
im_dst = im_dst + im_temp

Это означает, что im_dst имеет тип CV_8UC3 (или в numpy numpy.uint8), так как он копируется из кадра камеры. Это значения от 0 до 255. Затем вы добавляете оба изображения, которые имеют разные типы и разные диапазоны значений, что в конце дает изображение типа с плавающей запятой, но значения фона находятся в диапазоне от 0 до 255, который в imshow отображается как белый, если значение >= 1 для float введите изображения.

В хорошем случае тип тот же и этой проблемы не будет.


Решение:

Один из них:

im_src = np.uint8(outImage.copy() * 255)

но если вам не нужен outImage как float для чего-то другого, просто замените:

outImage = cv2.add(foreground, background)
outImage = outImage/255

для:

outImage = cv2.add(foreground, background, dtype=np.uint8)

Предложения:

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

1) Это:

    # Split png foreground image
    b, g, r, a = cv2.split(foreGroundImage)

    # Save the foregroung RGB content into a single object
    foreground = cv2.merge((b, g, r))

такой же как:

    # Split png foreground image
    a = foreGroundImage[:,:,3]

    # Save the foregroung RGB content into a single object
    foreground = foreGroundImage[:,:,0:3]

2) последнюю часть полностью можно сделать с изменением размера и копированием. Если вы не планируете поворачивать изображение или делать другие вещи, гомография — это излишество.

что-то типа:

im_dst[startY:endY, startX,endX] = cv2.resize(im_src, (endX-startX, endY-startY))
person api55    schedule 03.05.2019