Use opencv stitcher from python
Question: 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](http://docs.opencv.org/modules/stitching/doc/stitching.html) 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.


Answer: 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](http://algomorph.com/storage/pythonPortAux.h). 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);
        }
    }
    

