上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
以前、書いた投稿でSURFで二枚の画像での対応付けを行いました。

PythonからOpenCVのSURFを使う | 詠み人知らずの備忘録



SURFで抽出した対応する座標を用いれば、画像同士を合成することができます。
以下に参考にしたサイトです。


2枚の画像のモザイキング | Miyabiarts.net
前に紹介した「OpenCVを用いた特徴点対応付け」を応用することで複数の画像をモザイキングすることができます。 今回は、OpenCVを用いて下に示す2枚の画像をモザイキングして、1枚の大きな画像を作ります。 今回の例では、2枚の画像が大体半分程度重なり合ってないと上手くいかないです。 あと、画像合成の関係上、1枚目の画像に対して2枚目の画像が左側になるように撮影しないと正しい結果が得られませんので気をつけてください。



Panorama – Image Stitching in OpenCV | ramsrigoutham
he code snippet shown below is for simple image stitching of two images in OpenCV . It can easily be modified to stitch multiple images together and create a Panorama. OpenCV also has a stitching module which helps in achieving this task and which is more robust than this. The code presented here will help in understanding the major steps involved in image stitching algorithm. I am using OpenCV 2.4.3 and Visual studio 2010. This code is based on the openCV tutorial available here.



SURFを実行しても、surf結果画像のように完璧に対応点を抽出できません。 いくつか、誤検出が発生してしまいます。


surf結果画像
surf.png


そのため、誤検出を取り除く処理が必要になります。
RANSACが有名な方法のようです。

画像を合成する際に移動および回転などをさせるために射影変換を行いますが、OpenCVのfindhomographyでは、CV_RANSACを第3引数にすることにより、対応点をRANSACでふるいにかけてくれるようです。

ソース

途中まではSURFを求めるまでは前回までと同じソースを使用しています。
その座標を用いてHomography行列を求め合成しています。

# -*- coding: shift_jis -*-
# matching features of two images
import cv2
import sys
import scipy as sp
import numpy

if len(sys.argv) < 3:
    print 'usage: %s img1 img2' % sys.argv[0]
    sys.exit(1)

img1_path = sys.argv[1]
img2_path = sys.argv[2]

img1 = cv2.imread(img1_path, cv2.CV_LOAD_IMAGE_GRAYSCALE)
img2 = cv2.imread(img2_path, cv2.CV_LOAD_IMAGE_GRAYSCALE)

detector = cv2.FeatureDetector_create("SURF")
descriptor = cv2.DescriptorExtractor_create("BRIEF")
matcher = cv2.DescriptorMatcher_create("BruteForce-Hamming")

# detect keypoints
kp1 = detector.detect(img1)
kp2 = detector.detect(img2)

print '#keypoints in image1: %d, image2: %d' % (len(kp1), len(kp2))

# descriptors
k1, d1 = descriptor.compute(img1, kp1)
k2, d2 = descriptor.compute(img2, kp2)

print '#keypoints in image1: %d, image2: %d' % (len(d1), len(d2))

# match the keypoints
matches = matcher.match(d1, d2)

# visualize the matches
print '#matches:', len(matches)
dist = [m.distance for m in matches]

print 'distance: min: %.3f' % min(dist)
print 'distance: mean: %.3f' % (sum(dist) / len(dist))
print 'distance: max: %.3f' % max(dist)

# threshold: half the mean
thres_dist = (sum(dist) / len(dist)) * 0.9

# keep only the reasonable matches
sel_matches = [m for m in matches if m.distance < thres_dist]

print '#selected matches:', len(sel_matches)

point1 = [[k1[m.queryIdx].pt[0], k1[m.queryIdx].pt[1]] for m in sel_matches]
point2 = [[k2[m.trainIdx].pt[0], k2[m.trainIdx].pt[1]] for m in sel_matches]

point1 = numpy.array(point1)
point2 = numpy.array(point2)

H, Hstatus = cv2.findHomography(point2,point1,cv2.RANSAC)

# 移動量を算出
x=0
y=0
cnt=0
for i,v in enumerate(Hstatus):
    if v==1:
        x += point1[i][0]-point2[i][0]
        y += point1[i][1]-point2[i][1]
        cnt += 1

# カラー画像として改めて読み込む
img1 = cv2.imread(img1_path)
img2 = cv2.imread(img2_path)

x = abs(int(round(x/cnt)))
y = abs(int(round(y/cnt)))

# sizeを取得
h1, w1 = img1.shape[:2]
h2, w2 = img2.shape[:2]

dst = cv2.warpPerspective(img2,H,(w2+x,h2+y))

for i in xrange(w1):
    for j in xrange(h1):
        dst[j,i] = img1[j,i]

cv2.imshow("result", dst)
cv2.waitKey()



結果

入力画像
wall1.jpg


wall2.jpg


2枚目画像のHomography行列と演算結果
homography1.png


結果画像
homography2.png


2枚目画像のHomography行列と演算結果を見て分かるかもしれませんが、気づいたことを何点か・・・

このプログラムでは、1枚目の画像に合わせるように2枚目の画像を射影変換して合わせているため、 画像の左右の対応関係が逆だったりすると合成がうまくいかなかったり。
1枚目の画像より上部にある2枚目の画像が反映されていなかったりとあります。
#それは、理論というよりもプログラム上の問題かもしれませんが

ただ、複数枚つなげていくと誤差が積み重なっていきそうではあります。



スポンサーサイト
一生懸命POCを実装していましたが、
OpenCV内にphaseCorrelateとしてPOCが定義されているのを発見しました。

ヘルプなどあさってみましたが特に記載されていないため、
まだベータ版なのかもしれませんが問題なく動作していたためご紹介を

opencvフォルダ内の以下のディレクトリにそれぞれ格納されていました。
関数自体の定義
modules\imgproc\src\phasecorr.cpp
モジュール(?)のテスト
modules\imgproc\test\test_pc.cpp
使用方法
samples\cpp\phase_corr.cpp



ソース

phase_corr.cppをそのまま転載します。

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;

int main(int, char* [])
{
    VideoCapture video(0);
    Mat frame, curr, prev, curr64f, prev64f, hann;
    int key = 0;

    do
    {
        video >> frame;
        cvtColor(frame, curr, CV_RGB2GRAY);

        if(prev.empty())
        {
            prev = curr.clone();
            createHanningWindow(hann, curr.size(), CV_64F);
        }

        prev.convertTo(prev64f, CV_64F);
        curr.convertTo(curr64f, CV_64F);

        Point2d shift = phaseCorrelate(prev64f, curr64f, hann);
        double radius = cv::sqrt(shift.x*shift.x + shift.y*shift.y);

        if(radius > 5)
        {
            // draw a circle and line indicating the shift direction...
            Point center(curr.cols >> 1, curr.rows >> 1);
            cv::circle(frame, center, (int)radius, cv::Scalar(0, 255, 0), 3, CV_AA);
            cv::line(frame, center, Point(center.x + (int)shift.x, center.y + (int)shift.y), cv::Scalar(0, 255, 0), 3, CV_AA);
        }

        imshow("phase shift", frame);
        key = waitKey(2);

        prev = curr.clone();
    } while((char)key != 27); // Esc to exit...

    return 0;
}



27行目がPOCです。画像を渡すとPoint2dで座標が返却されます。
動作内容はカメラから画像(フレーム)を取得し、前のフレームと比較しカメラがどちらに動いているか判定するようです。
移動方向をフレーム中心の円の大きさを用いて表示するようになっております。

参考までにご紹介です。
今回は、Schimaの日記 - ステレオマッチング (cvaux)で紹介・実装されている。
OpenCVSharpでブロックマッチングを自分でも試してみました。

ソース

※処理の肝の部分だけ抜粋しています。


// 入力画像の読み込み
using (IplImage imgLeft = new IplImage("tsukuba_left.png", LoadMode.GrayScale))
using (IplImage imgRight = new IplImage("tsukuba_right.png", LoadMode.GrayScale))
{
    // 出力画像の領域を確保
    using (IplImage dstBM = new IplImage(imgLeft.Size, BitDepth.F32, 1))
    using (IplImage dst = new IplImage(imgLeft.Size, BitDepth.U8, 1))
    using (CvStereoBMState stateBM = new CvStereoBMState(StereoBMPreset.Narrow, 16))
    {
        Cv.FindStereoCorrespondenceBM(imgLeft, imgRight, dstBM, stateBM);
        Cv.ConvertScale(dstBM, dstBM, 16);
        
        Cv.FindStereoCorrespondence(imgLeft, imgRight, DisparityMode.Birchfield, dst, 50, 25, 5, 12, 15, 25);
        Cv.ConvertScale(dst, dst, 16);

        // ウィンドウに表示
        using (CvWindow window = new CvWindow("Stereo Correspondence", dst))
        using (CvWindow windowBM = new CvWindow("Stereo CorrespondenceBM", dstBM))
        {
            Cv.WaitKey();
        }
    }
}




結果

結果は以下のようになります。

【FindStereoCorrespondence】


【FindStereoCorrespondenceBM】
StereoBM.jpg



FindStereoCorrespondenceBMで処理された方の画像が白くなっているのはなぜだろう・・・
FindStereoCorrespondenceでしかされていない処理やアルゴリズム自体の精度の問題があるのかもしれません・・・


画像の形を変換する?
射影変換をやってみることに。。。


まずは、どんなものか感じをつかむために、こちらのサイトの画像をC#で書き直してみました。
OpenCV@Chihara-Lab. 射影変換

計算を自前でやろうとしたのですが、逆行列の計算が中々うまくいかず結局、OpenCvをC#で使用するOpenCvSharpというラッパーを使用しました(;´Д`)
OpenCVをC#やVB.NETなどの.NET Frameworkの言語から利用するためのクロスプラットフォームで動作するラッパーです。とのこと


ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus; 

namespace HomographyOpenCV
{
    class Program
    {
        static void Main(string[] args)
        {
            // 元画像の頂点の座標
            double[] a = new double[]{
                                93, 90,
                                20, 422,
                                501, 468,
                                408, 43
            };

            // 変換先の頂点の座標
            double[] b = new double[]{
                                0, 0,
                                0, 511,
                                511, 511,
                                511, 0
            };
            CvMat Mata = new CvMat(4, 2, MatrixType.F64C1, a);
            CvMat Matb = new CvMat(4, 2, MatrixType.F64C1, b);

            IplImage src = Cv.LoadImage("1.jpg", LoadMode.Color);
            IplImage dst = src.Clone();

            //homography
            CvMat h = new CvMat(3, 3, MatrixType.F64C1);

            Cv.FindHomography(Mata, Matb, h);

            for(int i=0 ; i<3 ;i++)
            {
                for(int j=0; j<3; j++)
                {
                    System.Diagnostics.Debug.WriteLine("h[{0},{1}] = {2}", i,j,h[i,j]);
                }
            }

            Cv.WarpPerspective(src, dst, h);

            using (new CvWindow("src", src))
            using (new CvWindow("dst", dst))
            {
                Cv.WaitKey();
            }

            src.Dispose();
            dst.Dispose();
        }
    }
}


結果

プログラムの結果はこんな感じになりました。
元画像
src.png

変換画像
dst.png

きちんと変換できてるようです。

FC2カウンター
プロフィール

Author:詠み人知らず
プログラム好きな名もなき凡人がお送りしています。(得意とは言っていない
最近の興味はPython、C#、Matlab(Octave)、画像処理、AR(拡張現実)、統計などなど・・・

気分で思いついたことを書くため話題に一貫性がないかもしれません。

カレンダー
08 | 2017/03 | 09
- - - 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 -
最新記事
タグクラウドとサーチ

カテゴリ
最新コメント
最新トラックバック
月別アーカイブ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。