[PYTHON] Correspondance d'images et alignement de groupes de points avec PatchMatch

Jouons avec PatchMatch.

Qu'est-ce que PatchMatch? (Grossièrement)

ダウンロード.png

Un aperçu approximatif de PatchMatch est Trouvez des similitudes entre une image et une autre! C'est un algorithme appelé.

Dans Photoshop, il est utilisé pour effacer des parties spécifiques d'une image. hqdefault.jpg

L'idée de la base est la suivante.

1. 1. Définissez aléatoirement la correspondance entre tous les pixels de l'image principale et l'image cible.
Finalement, cette paire de correspondances sera une paire de pixels similaires.

2. Basé sur l'idée que les pixels adjacents sont susceptibles de faire partie de la même pièce
Comparez la similitude actuelle avec la similitude des pixels adjacents,
S'il est plus grand que la similitude actuellement définie (similarité plus élevée), il est mis à jour à cette valeur.

3. 3. Répétez ci-dessous

PatchMatch est parfois utilisé comme correspondance de modèle, En outre, sur la base de l'idée commune que les pièces adjacentes seront les mêmes Le même côté est la même profondeur! De ce point de vue, Colmap est utilisé pour estimer la profondeur. C'est une théorie qui semble être largement applicable. tester.png

Essayez de faire correspondre les deux images.

Le point important de cet article est Si vous utilisez PatchMatch, vous pouvez voir des parties similaires dans deux images. Cela signifie qu'il peut être jugé au niveau du pixel.

ダウンロード.jpeg

Lorsque vous faites de la SfM, le mot correspondant à un point caractéristique apparaît souvent, Étant donné que la correspondance des points caractéristiques est un processus avec des points caractéristiques, Il est facilement affecté par l'environnement et si le nombre de points caractéristiques est petit, le traitement échouera.

Avec PatchMatch, vous pouvez augmenter le nombre de correspondances, non? Dans cet esprit, jouons avec le traitement suivant.

1. Exécutez Patch Match avec 2 images. Calculer un ensemble de pixels similaire à tous les pixels avec SAD
2. Exécutez Ransac sur les deux paires correspondantes. Supprimez les valeurs aberrantes.

Voyons avec quelle précision et de nombreux cas peuvent être mis en correspondance avec PatchMatch. Ensuite, je vais essayer de regrouper et d'aligner les points en utilisant l'image RGBD et les informations des paramètres internes. Si vous pouvez bien correspondre, Vous pourrez peut-être bien vous aligner sans être affecté par le bruit.

3. 3. Regroupement de points à partir d'un ensemble de pixels extraits
4. Aligner les points extraits
Aligner avec le groupe de points principal en utilisant le paramètre de 5.4

code

https://github.com/MingtaoGuo/PatchMatch

Le code ci-dessus est emprunté pour la partie de traitement de base. Si vous l'utilisez tel quel, le visage de Maria deviendra un avatar, donc Modifiez pour n'utiliser que le résultat de l'exécution de PatchMatch.

reconstruction.py



def reconstruction(f, A, B,AdepthPath="",BdepthPath=""):
    A_h = np.size(A, 0)
    A_w = np.size(A, 1)
    temp = np.zeros_like(A)

    srcA=[]
    dstB=[]

    colorA=np.zeros_like(A)
    colorB=np.zeros_like(A)

    for i in range(A_h):
        for j in range(A_w):
            colorA[i, j, :] = A[[i],[j],:]
            colorB[i, j, :] = B[f[i, j][0], f[i, j][1], :]

            temp[i, j, :] = B[f[i, j][0], f[i, j][1], :]
            srcA.append([i,j])
            dstB.append([f[i, j][0], f[i, j][1]])

    #Ensuite, appliquez les pixels similaires obtenus par Patch Match.
    #Une image qui reconstruit l'image A en utilisant les pixels de l'image B
    cv2.imwrite('colorB.jpg', colorB)
    cv2.imwrite('colorA.jpg', colorA)

    src=np.array(srcA)
    dst=np.array(dstB)
    print(src.shape)
    print(dst.shape)

    srcA_pts = src.reshape(-1, 1, 2)
    dstB_pts = dst.reshape(-1, 1, 2)
    # RANSAC
    print(srcA_pts.shape)
    print(dstB_pts.shape)

    M, mask = cv2.findHomography(srcA_pts, dstB_pts, cv2.RANSAC, 5.0)
    matchesMask = mask.ravel().tolist()

    im_h = cv2.hconcat([A,B])
    cv2.imwrite('outputMerge.jpg', im_h)

    outputA = np.zeros_like(A)
    outputB = np.zeros_like(A)

    for i in range(srcA_pts.shape[0]):
        if mask[i] == 0: continue

        srcIm = [srcA_pts[i][0][0],srcA_pts[i][0][1]]
        dstIm = [dstB_pts[i][0][0],dstB_pts[i][0][1]]

        outputA[srcIm[0],srcIm[1],:]=A[srcIm[0],srcIm[1],:]
        outputB[dstIm[0],dstIm[1],:]=B[dstIm[0],dstIm[1],:]

    im_h = cv2.hconcat([outputA, outputB])
    cv2.imwrite('outputMatch.jpg', im_h)D

    im_h = cv2.hconcat([A, B])

    for i in range(srcA_pts.shape[0]):
        if mask[i] == 0: continue

        srcIm = [srcA_pts[i][0][0],srcA_pts[i][0][1]]
        dstIm = [dstB_pts[i][0][0],dstB_pts[i][0][1]]
        cv2.line(im_h,   (srcIm[0], srcIm[1]),
                        (dstIm[0]+int(1280 * 0.3), dstIm[1]),
                        (0, 255, 0), thickness=1, lineType=cv2.LINE_4)

    cv2.imwrite('outputMatchAddLine.jpg', im_h)

    if AdepthPath!="":
        #S'il y a des données de profondeur d'image / des paramètres internes,
        #Essayez de regrouper les points et de vous aligner sur les informations correspondantes.
        #PatchMatch uniquement A Pas besoin du traitement suivant

        import open3d as o3d
        def CPD_rejister(source, target):
            from probreg import cpd
            import copy
            type = 'rigid'
            tf_param, _, _ = cpd.registration_cpd(source, target, type)

            result = copy.deepcopy(source)
            result.points = tf_param.transform(result.points)

            return result, tf_param.rot, tf_param.t, tf_param.scale

        def Register(pclMVS_Main, pclMVS_Target):
            # CPD : step1 Run CPD for SfM Data
            result, rot, t, scale = CPD_rejister(pclMVS_Target, pclMVS_Main)

            # CPD : step2 Apply CPD result for MVS Data
            lastRow = np.array([[0, 0, 0, 1]])
            ret_R = np.array(rot)
            ret_t = np.array([t])
            ret_R = scale * ret_R

            transformation = np.concatenate((ret_R, ret_t.T), axis=1)
            transformation = np.concatenate((transformation, lastRow), axis=0)
            return transformation, rot, t, scale

        def getPLYfromNumpy_RGB(nplist, colorList):
            # nplist = np.array(nplist)
            pcd = o3d.geometry.PointCloud()
            pcd.points = o3d.utility.Vector3dVector(nplist)
            pcd.colors = o3d.utility.Vector3dVector(colorList)
            return pcd

        def numpy2Dto1D(arr):
            if type(np.array([])) != type(arr):
                arr = np.array(arr)
            if arr.shape == (3, 1):
                return np.array([arr[0][0], arr[1][0], arr[2][0]])
            if arr.shape == (2, 1):
                return np.array([arr[0][0], arr[1][0]])
            else:
                assert False, "numpy2Dto1D:Pas compatible"

        def TransformPointI2C(pixel2D, K):
            X = float(float(pixel2D[0] - K[0][2]) * pixel2D[2] / K[0][0])
            Y = float(float(pixel2D[1] - K[1][2]) * pixel2D[2] / K[1][1])
            Z = pixel2D[2]
            CameraPos3D = np.array([[X], [Y], [Z]])
            return CameraPos3D

        def getDepthCSPerView(path):
            import csv
            #acquisition de données de profondeur
            in_csvPath = path
            with open(in_csvPath) as f:
                reader = csv.reader(f)
                csvlist = [row for row in reader]

            return csvlist

        #données de profondeur(CSV)
        Adepthlist = getDepthCSPerView(AdepthPath)
        Bdepthlist = getDepthCSPerView(BdepthPath)
        pclA=[]
        pclB=[]
        colorA=[]
        colorB=[]

        ALLpclA=[]
        ALLpclB=[]
        ALLcolorA=[]
        ALLcolorB=[]

        #La valeur de profondeur est 1.Restaurer la portée de 5 m au groupe de points
        depth_threshold = 1.5  #Mètre
        depth_scale = 0.0002500000118743628
        threshold = depth_threshold / depth_scale

        #Paramètres internes de la caméra RVB
        # width: 1280, height: 720, ppx: 648.721, ppy: 365.417, fx: 918.783, fy: 919.136,
        retK = np.array([[918.783, 0, 648.721],
                         [0, 919.136, 365.417],
                         [0, 0, 1]])


        cnt = 0

        for y in range(len(Adepthlist)):
            for x in range(len(Adepthlist[0])):
                ADepth = float(Adepthlist[y][x])
                BDepth = float(Bdepthlist[y][x])

                if (ADepth == 0 or ADepth > threshold) or (BDepth == 0 or BDepth > threshold):
                    continue

                AXYZ = TransformPointI2C([x,y, ADepth], retK)
                BXYZ = TransformPointI2C([x,y, BDepth], retK)

                ALLpclA.append(numpy2Dto1D(AXYZ))
                ALLpclB.append(numpy2Dto1D(BXYZ))
                color = A[int(srcIm[0])][int(srcIm[1])]
                ALLcolorA.append([float(color[0] / 255), float(color[1] / 255), float(color[2] / 255)])

                color = B[int(dstIm[0])][int(dstIm[1])]
                ALLcolorB.append([float(color[0] / 255), float(color[1] / 255), float(color[2] / 255)])

        ALLpclA = getPLYfromNumpy_RGB(ALLpclA,ALLcolorA)
        ALLpclB = getPLYfromNumpy_RGB(ALLpclB,ALLcolorB)
        o3d.io.write_point_cloud("ALL_pclA_Before.ply", ALLpclA)
        o3d.io.write_point_cloud("ALL_pclB_Before.ply", ALLpclB)

        for i in range(srcA_pts.shape[0]):
            if mask[i] == 0: continue

            srcIm = [srcA_pts[i][0][0], srcA_pts[i][0][1]]
            dstIm = [dstB_pts[i][0][0], dstB_pts[i][0][1]]

            ADepth = float(Adepthlist[int(srcIm[0])][int(srcIm[1])])
            BDepth = float(Bdepthlist[int(dstIm[0])][int(dstIm[1])])


            if (ADepth == 0 or ADepth > threshold) or (BDepth == 0 or BDepth > threshold):
                continue

            AXYZ = TransformPointI2C([int(srcIm[1]), int(srcIm[0]), ADepth], retK)
            BXYZ = TransformPointI2C([int(dstIm[1]), int(dstIm[0]), BDepth], retK)

            pclA.append(numpy2Dto1D(AXYZ))
            pclB.append(numpy2Dto1D(BXYZ))
            color = A[int(srcIm[0])][int(srcIm[1])]
            colorA.append([float(color[0] / 255), float(color[1] / 255), float(color[2] / 255)])

            color = B[int(dstIm[0])][int(dstIm[1])]
            colorB.append([float(color[0] / 255), float(color[1] / 255), float(color[2] / 255)])


        pclA = getPLYfromNumpy_RGB(pclA,colorA)
        pclB = getPLYfromNumpy_RGB(pclB,colorB)

        o3d.io.write_point_cloud("pclA_Before.ply", pclA)
        o3d.io.write_point_cloud("pclB_Before.ply", pclB)

        trans, rot, t, scale = Register(pclA, pclB)
        pclB.transform(trans)

        o3d.io.write_point_cloud("pclA_After.ply", pclA)
        o3d.io.write_point_cloud("pclB_After.ply", pclB)

        ALLpclB.transform(trans)
        o3d.io.write_point_cloud("ALL_pclA_After.ply", ALLpclA)
        o3d.io.write_point_cloud("ALL_pclB_After.ply", ALLpclB)

Résultat: correspondance d'image

Je vais essayer.

outputMerge.jpg

C'est

outputMatchAddLine.jpg

comme ça. Je ne comprends pas vraiment. Extrait et affiche les pixels qui ont été mis en correspondance et les valeurs aberrantes ont été supprimées par ransac.

outputMatch.jpg

Je ne sais pas plus que non. Mais d'une manière ou d'une autre, les formes semblent correspondre. Je pense qu'il peut être utilisé pour le suivi, la détection de mouvement, etc.

Résultat: alignement du groupe de points

Je ne sais pas si la réponse sera la même que le résultat précédent, mais Alignons les groupes de points des deux points de vue en fonction des informations extraites.

3. 3. Regroupement de points à partir d'un ensemble de pixels extraits
4. Aligner les points extraits
Aligner avec le groupe de points principal en utilisant le paramètre de 5.4

C'est 無題.png

comme ça. 無題.png

Hmm! C'est subtil! c'est tout.

[Une addition] 無題.png

Le résultat précédent n'était pas précis en profondeur Je l'ai vérifié avec une autre donnée.

C'est un groupe de points plein de bruit, Est-ce parce que la correspondance bidimensionnelle est réussie? J'ai pu bien correspondre naturellement.

J'ai utilisé uniquement Ransac pour supprimer les valeurs aberrantes lors de la correspondance, Puisque j'ai fait un groupe de points, la prochaine fois, je considérerai la distance des points correspondants Essayez de supprimer les valeurs aberrantes.

Recommended Posts

Correspondance d'images et alignement de groupes de points avec PatchMatch
Nuage de points avec du poivre
Traitement d'image avec MyHDL
Reconnaissance d'image avec keras
Traitement d'image avec Python
J'ai essayé de trouver la matrice affine dans l'alignement de l'image (correspondance des points caractéristiques) en utilisant la transformation affine
Traitement d'image avec PIL
Acquisition d'un groupe de points 3D avec Pepper de Softbank (Choregraphe, Python)