Projector-Camera Calibration - the "easy" way

First let me open by saying projector-camera calibration is NOT EASY. But it's technically not complicated too.

It is however, an amalgamation of optimizations that accrue and accumulate error with each step, so that the end product is not far from a random guess.
So 3D reconstructions I was able to get from my calibrated pro-cam were just a distorted mess of points.

Nevertheless, here come the deets.
Continue reading "Projector-Camera Calibration - the "easy" way"

Share

Revisiting graph-cut segmentation with SLIC and color histograms [w/Python]

As part of the computer vision class I'm teaching at SBU I asked students to implement a segmentation method based on SLIC superpixels. Here is my boilerplate implementation.

This follows the work I've done a very long time ago (2010) on the same subject.

For graph-cut I've used PyMaxflow: https://github.com/pmneila/PyMaxflow, which is very easily installed by just pip install PyMaxflow

The method is simple:

  • Calculate SLIC superpixels (the SKImage implementation)
  • Use markings to determine the foreground and background color histograms (from the superpixels under the markings)
  • Setup a graph with a straightforward energy model: Smoothness term = K-L-Div between superpix histogram and neighbor superpix histogram, and Match term = inf if marked as BG or FG, or K-L-Div between SuperPix histogram and FG and BG.
  • To find neighbors I've used Delaunay tessellation (from scipy.spatial), for simplicity. But a full neighbor finding could be implemented by looking at all the neighbors on the superpix's boundary.
  • Color histograms are 2D over H-S (from the HSV)

Result

Share

OMG CMake/OpenCV3 can you be more difficult? Linking order problems with OpenNI2...

So I just spent 1.5 hours figuring this out.
Compiling an example on Ubuntu 16.04 with OpenCV built from scratch with OpenNI2 support.
(OpenNI2 is from Orbbec, but that doesn't make any difference: https://orbbec3d.com/develop/)

When using this straightforward CMake script for compilation - it doesn't work:

cmake_minimum_required(VERSION 3.2)
project(MyApp)

find_package(OpenCV 3 REQUIRED)

set(OPENNI2_LIBS "OpenNI2")
link_directories("/home/user/Downloads/2-Linux/OpenNI-Linux-x64-2.3/Redist")

add_executable(myapp main.cpp)
target_link_libraries(myapp ${OpenCV_LIBS} ${OPENNI2_LIBS})

Complains of undefined references:

/usr/bin/c++   -g   CMakeFiles/myapp.dir/main.cpp.o  -o myapp  -L/home/user/Downloads/2-Linux/OpenNI-Linux-x64-2.3/Redist -rdynamic -lOpenNI2 /usr/local/lib/libopencv_shape.so.3.2.0 /usr/local/lib/libopencv_stitching.so.3.2.0 /usr/local/lib/libopencv_superres.so.3.2.0 /usr/local/lib/libopencv_videostab.so.3.2.0 /usr/local/lib/libopencv_objdetect.so.3.2.0 /usr/local/lib/libopencv_calib3d.so.3.2.0 /usr/local/lib/libopencv_features2d.so.3.2.0 /usr/local/lib/libopencv_flann.so.3.2.0 /usr/local/lib/libopencv_highgui.so.3.2.0 /usr/local/lib/libopencv_ml.so.3.2.0 /usr/local/lib/libopencv_photo.so.3.2.0 /usr/local/lib/libopencv_video.so.3.2.0 /usr/local/lib/libopencv_videoio.so.3.2.0 /usr/local/lib/libopencv_imgcodecs.so.3.2.0 /usr/local/lib/libopencv_imgproc.so.3.2.0 /usr/local/lib/libopencv_core.so.3.2.0 -Wl,-rpath,/home/user/Downloads/2-Linux/OpenNI-Linux-x64-2.3/Redist:/usr/local/lib 
/usr/local/lib/libopencv_videoio.so.3.2.0: undefined reference to `oniStreamGetProperty'
/usr/local/lib/libopencv_videoio.so.3.2.0: undefined reference to `oniRecorderDestroy'
/usr/local/lib/libopencv_videoio.so.3.2.0: undefined reference to `oniDeviceIsCommandSupported'
/usr/local/lib/libopencv_videoio.so.3.2.0: undefined reference to `oniDeviceSetProperty'

You'll notice that -lOpenNI2 does indeed appear for correct linking.
The linker doesn't complain that lib was not found - it just misses the references.
This lead me to understand it's a linking order problem (after ~45 minutes of banging my head vs. the keyboard and swearing profusely).

Some more swearing and head banging got me to understand that CMake is messing around with the link order.
So even if try:

target_link_libraries(myapp ${OpenCV_LIBS} ${OPENNI2_LIBS} ${OpenCV_LIBS} ${OPENNI2_LIBS})

i.e. making the order effectively meaningless -- it still doesn't work!

More swearing and head banging, another ~40 minutes passed, and I figured out a solution.
The real solution is to slap someone in CMake in the face with a trout, but here's a solution to my problem:

find_package(OpenCV 3 REQUIRED core highgui videoio) # ORDER MATTERS!!! videoio must be last!
set(OpenCV_LIBS "${OpenCV_LIBS};OpenNI2") #add openni2 at the end (although cmake doesn't keep order anyway)
target_link_libraries(myapp ${OpenCV_LIBS})

Now it compiles.

And look at the make VERBOSE=1:

/usr/bin/c++   -g   CMakeFiles/myapp.dir/main.cpp.o  -o myapp  -L/home/user/Downloads/2-Linux/OpenNI-Linux-x64-2.3/Redist -rdynamic /usr/local/lib/libopencv_highgui.so.3.2.0 /usr/local/lib/libopencv_videoio.so.3.2.0 -lOpenNI2 /usr/local/lib/libopencv_core.so.3.2.0 -Wl,-rpath,/home/user/Downloads/2-Linux/OpenNI-Linux-x64-2.3/Redist:/usr/local/lib -Wl,-rpath-link,/usr/local/lib 

Can you see how highgui and videoio are before OpenNI2, and core is after?
Why? Whhhhhhy?

The key is to get OpenNI to be linked in order after videoio.

OMG CMake, OMG OpenCV, OMG you gaiz, W-T-F?

Update:
This method breaks down as soon as more OpenCV components are added. The order goes haywire again, and OpenNI2 comes before videoio, which breaks the link.
As of now the way I can compile it is like so:

set(LINK_LIBS /usr/local/lib/libopencv_core.so.3.2
              /usr/local/lib/libopencv_highgui.so.3.2
              /usr/local/lib/libopencv_videoio.so.3.2
              /usr/local/lib/libopencv_imgproc.so.3.2
              /usr/local/lib/libopencv_calib3d.so.3.2
              OpenNI2)
Share

New edition to the Mastering OpenCV book - now with OpenCV3!

Mastering OpenCV 3
I'm happy to announce that the new edition of Mastering OpenCV is out!
You can get it on Amazon: Mastering OpenCV 3

It brings up most of the older OpenCV2 book projects to OpenCV3, including my Toy-SfM (or "Exploring SfM") project.

A lot has happened in the OpenCV3 APIs with respect to Structure from Motion.
It got much easier!
The book chapter on SfM is a gentle introduction to the subject, that focuses on coding and the core concepts, while abstracting on the math.

Thanks for listening!
Roy

Share

WTH OpenGL 4? Rendering elements arrays with VAOs and VBOs in a QGLWidget

I spent an entire day getting OpenGL 4 to display data from a VAO with VBOs so I thought I'd share the results with you guys, save you some pain.

I'm using the excellent GL wrappers from Qt, and in particular QGLShaderProgram.
This is pretty straightforward, but the thing to remember is that OpenGL is looking for the vertices/other elements (color? tex coords?) to come from some bound GL buffer or from the host. So if your app is not working and nothing appears on screen, just make sure GL has a bound buffer and the shader locations match up and consistent (see the const int I have on the class here).

Share

[Python] OpenCV capturing from a v4l2 device

I tried to set the capture format on a webcam from OpenCV's cv2.VideoCapture and ran into a problem: it's using the wrong IOCTL command.
So I used python-v4l2capture to get images from the device, which allows more control.
Here is the gist:

Enjoy!
Roy

Share

OpenCV Python YAML persistance

I wasn't able to find online a complete example on how to persist OpenCV matrices in Python (so really NumPy arrays) to YAML like what cv::FileStorage will give you.

So here's a short snippet:

import numpy as np
import yaml

# A yaml constructor is for loading from a yaml node.
# This is taken from: http://stackoverflow.com/a/15942429
def opencv_matrix_constructor(loader, node):
    mapping = loader.construct_mapping(node, deep=True)
    mat = np.array(mapping["data"])
    mat.resize(mapping["rows"], mapping["cols"])
    return mat
yaml.add_constructor(u"tag:yaml.org,2002:opencv-matrix", opencv_matrix_constructor)

# A yaml representer is for dumping structs into a yaml node.
# So for an opencv_matrix type (to be compatible with c++'s FileStorage) we save the rows, cols, type and flattened-data
def opencv_matrix_representer(dumper, mat):
    mapping = {'rows': mat.shape[0], 'cols': mat.shape[1], 'dt': 'd', 'data': mat.reshape(-1).tolist()}
    return dumper.represent_mapping(u"tag:yaml.org,2002:opencv-matrix", mapping)
yaml.add_representer(np.ndarray, opencv_matrix_representer)


#example
with open('output.yaml', 'w') as f:
    f.write("%YAML:1.0")
    yaml.dump({"a matrix": np.zeros((10,10)), "another_one": np.zeros((2,4))}, f)

#   a matrix: !!opencv-matrix
#     cols: 10
#     data: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#       0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#       0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#       0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#       0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#       0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
#       0.0, 0.0, 0.0, 0.0, 0.0]
#     dt: d
#     rows: 10
#   another_one: !!opencv-matrix
#     cols: 4
#     data: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
#     dt: d
#     rows: 2

with open('output.yaml', 'r') as f:
    print yaml.load(f)
  
#  {'a matrix': array([[ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]]), 'another_one': array([[ 0.,  0.,  0.,  0.],
#         [ 0.,  0.,  0.,  0.]])}

There you go

Share