Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use opencv stitcher from python

OpenCV can be used with the pythonbindings and it works quite well. However I was wondering (hoping really) whether it is possible to use OpenCv's stitcher in python as well. I've tried several things but wasn't able to get it to work. If it is at all possible I probably need to do an extra import but I can't figure it out and google doesn't give me the answer either. Hope there's a opencv-python guru among you who can help me out.

like image 819
SeeDoubleYou Avatar asked Nov 14 '22 00:11

SeeDoubleYou


1 Answers

Okay, so, I figured it out finally. So far I've only ported the stitch method with two arguments, ask me if you have trouble exposing whatever else you might need.

The easiest way to build it is by compiling everything in a position-independent way (-fPIC option for gcc) into a dynamic library, whilst linking opencv_core and opencv_stitching libraries. You'll have to also add the include directory for whatever version of python you're building against, so it can find the correct Python.h header.

If you build correctly, you'll be able to use the compiled library the same way as you would use a python module.

Unfortunately, since they don't provide access to the constructor, I've had to settle for making a global instance of the thing. If there's another elegant way, I'm all ears (eyes). This means whenever you call the .Stitcher() constructor it will return the same instance (there's a separate one that tries to use GPU during construction, use .Stitcher(True) for that).

Here's my pythonPort.h file:

/* 
 * File:   pythonPort.h
 * Author: algomorph
 *
 * Created on December 5, 2012, 10:18 AM
 */

#ifndef PYTHONPORT_H
#define PYTHONPORT_H

#define MODULESTR "mycv"


#include "Python.h"
#include "numpy/ndarrayobject.h"
#include <opencv2/core/core.hpp>
#include <opencv2/stitching/stitcher.hpp>
/*
//include your own custom extensions here
#include "savgol.h"
#include "filters.hpp"
*/
#include "pythonPortAux.h"

#endif

MODULESTR should be whatever you want to name your module. I've kept it the same as the name of the library it compiles.

You'll have to copy whatever opencv_to and opencv_from routines you need from the cv2.cpp file and put into something like my pythonPortAux.h. Mine has lots of routines from there, you can find it at this link. MKTYPE2 macro is also there.

The rest is here in the pythonPort.cpp file below (I have other things there, this is just the Stitcher-relevant portion):

#include "pythonPort.h"

struct pycvex_Stitcher_t
{
    PyObject_HEAD
    Ptr<cv::Stitcher> v;
};

static PyTypeObject pycvex_Stitcher_Type =
{
    PyObject_HEAD_INIT(&PyType_Type)
    0,
    MODULESTR".Stitcher",
    sizeof(pycvex_Stitcher_t),
};

static void pycvex_Stitcher_dealloc(PyObject* self)
{
    //((pycvex_Stitcher_t*)self)->v.release();
    PyObject_Del(self);
}

static PyObject* pyopencv_from(const Ptr<cv::Stitcher>& r)
{
    pycvex_Stitcher_t *m = PyObject_NEW(pycvex_Stitcher_t, &pycvex_Stitcher_Type);
    new (&(m->v)) Ptr<cv::Stitcher>(); // init Ptr with placement new
    m->v = r;
    return (PyObject*)m;
}

static bool pyopencv_to(PyObject* src, Ptr<cv::Stitcher>& dst, const char* name="<unknown>")
{
    if( src == NULL || src == Py_None )
        return true;
    if(!PyObject_TypeCheck(src, &pycvex_Stitcher_Type))
    {
        failmsg("Expected cv::Stitcher for argument '%s'", name);
        return false;
    }
    dst = ((pycvex_Stitcher_t*)src)->v;
    return true;
}

static PyObject* pycvex_Stitcher_repr(PyObject* self)
{
    char str[1000];
    sprintf(str, "<Stitcher %p>", self);
    return PyString_FromString(str);
}

Stitcher gStitcher = cv::Stitcher::createDefault(false);
Stitcher gStitcherGPU = cv::Stitcher::createDefault(true);

static PyObject* pycvex_Stitcher_Stitcher(PyObject* , PyObject* args, PyObject* kw)
{
    PyErr_Clear();
    {
        pycvex_Stitcher_t* self = 0;
        bool try_use_gpu = false;

        const char* keywords[] = { "img", "pt1", "pt2","connectivity","leftToRight", NULL };
        if (PyArg_ParseTupleAndKeywords(args, kw, "|b:Stitcher",
                (char**) keywords, &try_use_gpu)){
            self = PyObject_NEW(pycvex_Stitcher_t, &pycvex_Stitcher_Type);
            if (self)
                ERRWRAP2(
                        if(try_use_gpu)
                            self->v = &gStitcherGPU;
                        else
                            self->v = &gStitcher;
                        );
            return (PyObject*) self;
        }
    }
    return NULL;
}
static PyGetSetDef pycvex_Stitcher_getseters[] =
{
    {NULL}  /* Sentinel */
};

static PyObject* pycvex_Stitcher_stitch(PyObject* self, PyObject* args, PyObject* kw){
    if(!PyObject_TypeCheck(self, &pycvex_Stitcher_Type))
        return failmsgp("Incorrect type of self (must be 'Stitcher' or its derivative)");
    Stitcher* _self_ = ((pycvex_Stitcher_t*)self)->v;
    //Stitcher::Status status;
    int status;

    PyObject* pyobj_images = NULL;
    vector<Mat> images = vector<Mat>();
    Mat pano;

    const char* keywords[] = { "images", NULL };
    if( PyArg_ParseTupleAndKeywords(args, kw, "O:Stitcher.stitch", (char**)keywords, &pyobj_images) &&
        pyopencv_to(pyobj_images, images, ArgInfo("images", false)))
    {
        ERRWRAP2( status = (int)_self_->stitch(images, pano));
        return Py_BuildValue("(NN)", pyopencv_from(status), pyopencv_from(pano));
    }

    return NULL;
}

static PyMethodDef pycvex_Stitcher_methods[] =
{
    {"stitch", (PyCFunction)pycvex_Stitcher_stitch, METH_KEYWORDS, "stitch(image) -> status, pano"},
    {NULL, NULL}
};

static void pycvex_Stitcher_specials(void)
{
    pycvex_Stitcher_Type.tp_base = NULL;
    pycvex_Stitcher_Type.tp_dealloc = pycvex_Stitcher_dealloc;
    pycvex_Stitcher_Type.tp_repr = pycvex_Stitcher_repr;
    pycvex_Stitcher_Type.tp_getset = pycvex_Stitcher_getseters;
    pycvex_Stitcher_Type.tp_methods = pycvex_Stitcher_methods;
}


static PyMethodDef methods[] = {
    {"Stitcher",(PyCFunction)pycvex_Stitcher_Stitcher, METH_KEYWORDS, "Stitcher([tryUseGpu=False]) -> <Stitcher object>"},
    {NULL, NULL}
};

extern "C"{
    #if defined WIN32 || defined _WIN32
    __declspec(dllexport)
    #endif
    void initcvex()
    {
      MKTYPE2(Stitcher);
      import_array();
      PyObject* m = Py_InitModule(MODULESTR, methods);
      PyObject* d = PyModule_GetDict(m);
      //PyDict_SetItemString(d, "__version__", PyString_FromString(CV_VERSION))
      opencv_error = PyErr_NewException((char*)MODULESTR".error", NULL, NULL);
      PyDict_SetItemString(d, "error", opencv_error);
    }
}
like image 72
Greg Kramida Avatar answered Dec 06 '22 21:12

Greg Kramida