├── .gitignore
├── README.rst
├── android
├── .project
├── AndroidManifest.xml
├── jni
│ ├── Android.mk
│ ├── Application.mk
│ └── libseg.cc
├── project.properties
└── src
│ └── net
│ └── fhtagn
│ └── libseg
│ └── SimpleMatter.java
├── include
├── api.h
├── geodesic.h
├── kde.h
├── matting.h
└── utils.h
├── screenshot.png
├── src
├── api.cc
├── geodesic.cc
├── geodesic_test.cc
├── kde.cc
├── kde_test.cc
├── matting.cc
└── third_party
│ └── miniglog
│ ├── README.rst
│ └── glog
│ ├── logging.cc
│ └── logging.h
└── unix
├── build.sh
├── clean.sh
├── gdb.sh
├── gen_ninja.sh
├── plot_densities.py
├── project.gyp
├── run.sh
├── samples
├── cvutils.cc
├── cvutils.h
├── interactive.cc
├── main.cc
└── simple_interactive.cc
├── third_party
└── gmock-1.7.0
│ ├── CHANGES
│ ├── CONTRIBUTORS
│ ├── LICENSE
│ ├── README
│ ├── gtest
│ ├── CHANGES
│ ├── CONTRIBUTORS
│ ├── LICENSE
│ ├── README
│ ├── include
│ │ └── gtest
│ │ │ ├── gtest-death-test.h
│ │ │ ├── gtest-message.h
│ │ │ ├── gtest-param-test.h
│ │ │ ├── gtest-param-test.h.pump
│ │ │ ├── gtest-printers.h
│ │ │ ├── gtest-spi.h
│ │ │ ├── gtest-test-part.h
│ │ │ ├── gtest-typed-test.h
│ │ │ ├── gtest.h
│ │ │ ├── gtest_pred_impl.h
│ │ │ ├── gtest_prod.h
│ │ │ └── internal
│ │ │ ├── gtest-death-test-internal.h
│ │ │ ├── gtest-filepath.h
│ │ │ ├── gtest-internal.h
│ │ │ ├── gtest-linked_ptr.h
│ │ │ ├── gtest-param-util-generated.h
│ │ │ ├── gtest-param-util-generated.h.pump
│ │ │ ├── gtest-param-util.h
│ │ │ ├── gtest-port.h
│ │ │ ├── gtest-string.h
│ │ │ ├── gtest-tuple.h
│ │ │ ├── gtest-tuple.h.pump
│ │ │ ├── gtest-type-util.h
│ │ │ └── gtest-type-util.h.pump
│ └── src
│ │ ├── gtest-all.cc
│ │ ├── gtest-death-test.cc
│ │ ├── gtest-filepath.cc
│ │ ├── gtest-internal-inl.h
│ │ ├── gtest-port.cc
│ │ ├── gtest-printers.cc
│ │ ├── gtest-test-part.cc
│ │ ├── gtest-typed-test.cc
│ │ ├── gtest.cc
│ │ └── gtest_main.cc
│ ├── include
│ └── gmock
│ │ ├── gmock-actions.h
│ │ ├── gmock-cardinalities.h
│ │ ├── gmock-generated-actions.h
│ │ ├── gmock-generated-actions.h.pump
│ │ ├── gmock-generated-function-mockers.h
│ │ ├── gmock-generated-function-mockers.h.pump
│ │ ├── gmock-generated-matchers.h
│ │ ├── gmock-generated-matchers.h.pump
│ │ ├── gmock-generated-nice-strict.h
│ │ ├── gmock-generated-nice-strict.h.pump
│ │ ├── gmock-matchers.h
│ │ ├── gmock-more-actions.h
│ │ ├── gmock-more-matchers.h
│ │ ├── gmock-spec-builders.h
│ │ ├── gmock.h
│ │ └── internal
│ │ ├── gmock-generated-internal-utils.h
│ │ ├── gmock-generated-internal-utils.h.pump
│ │ ├── gmock-internal-utils.h
│ │ └── gmock-port.h
│ └── src
│ ├── gmock-all.cc
│ ├── gmock-cardinalities.cc
│ ├── gmock-internal-utils.cc
│ ├── gmock-matchers.cc
│ ├── gmock-spec-builders.cc
│ ├── gmock.cc
│ └── gmock_main.cc
└── valgrind.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | third_party/figtree-0.9.3*
2 | third_party/gmock-1.7.0*
3 | third_party/glog-0.3.3*
4 | third_party/_install
5 | third_party/opencv*
6 | *.txt
7 | core
8 | data
9 | out
10 | unix/env.sh
11 | unix/third_party/gmock-1.7.0/Makefile
12 | unix/third_party/gmock-1.7.0/build-aux/*
13 | unix/third_party/gmock-1.7.0/config.log
14 | unix/third_party/gmock-1.7.0/config.status
15 | unix/third_party/gmock-1.7.0/libtool
16 | unix/third_party/gmock-1.7.0/scripts/gmock-config
17 | unix/third_party/gmock-1.7.0/gtest/Makefile
18 | unix/third_party/gmock-1.7.0/gtest/build-aux/*
19 | unix/third_party/gmock-1.7.0/gtest/config.log
20 | unix/third_party/gmock-1.7.0/gtest/config.status
21 | unix/third_party/gmock-1.7.0/gtest/libtool
22 | unix/third_party/gmock-1.7.0/gtest/scripts/gtest-config
23 | unix/third_party/opencv-2.4.8/
24 | *.deps
25 | android/bin
26 | android/gen
27 | android/libs
28 | android/obj
29 |
30 | # built application files
31 | *.apk
32 | *.ap_
33 |
34 | # files for the dex VM
35 | *.dex
36 |
37 | # Java class files
38 | *.class
39 |
40 | local.properties
41 | android/.classpath
42 | android/.settings
43 | android/.cproject
44 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | libseg
2 | ======
3 |
4 | This is an *UNFINISHED* implementation and it is still quite buggy.
5 |
6 | An interactive image foreground/background segmentation library.
7 |
8 | For now, this is an implementation of geodesic matting of [Bai09]. It doesn't
9 | implement the alpha matting part and just stops at the foreground/background
10 | segmentation stage.
11 |
12 | This library can be build for the Linux/OSX and for android.
13 |
14 | Its only dependency is the FIGTree library[1]. A version adapted for android
15 | (and still working on the desktop) is available here :
16 | TODO: include figtree-android
17 |
18 | .. image:: screenshot.png
19 | :width: 90%
20 |
21 |
22 | Linux/OSX
23 | ---------
24 | Linux/OSX build files and scripts are under unix/. Gyp (generate your project)
25 | and ninja are used to build the projects.
26 |
27 | Example programs are under unix/samples
28 |
29 | Note that the Linux/OSX example programs require OpenCV. libseg itself
30 | doesn't depend on OpenCV.
31 |
32 | Build instruction :
33 |
34 | cd third_party/gmock-1.7.0
35 | ./configure
36 | make
37 |
38 | cd unix
39 | # Point FIGTREE to figtree-android
40 | export FIGTREE=../../figtree-android ./gen_ninja.sh
41 | ./build.sh
42 |
43 | Android
44 | -------
45 | Android build files are under jni/.
46 |
47 | Build instructions
48 | ------------------
49 |
50 | Use the run script to run a binary (it sets up the correct LD_LIBRARY_PATH) :
51 |
52 | ./run.sh out/Default/tests
53 | ./run.sh out/Default/interactive
54 |
55 | References
56 | ==========
57 | [1] http://www.umiacs.umd.edu/~morariu/figtree/
58 |
59 | [Bai09] "Geodesic Matting: A Framework for Fast Interactive Image
60 | and Video Segmentation and Matting", Int J Comput Vis'09
61 |
--------------------------------------------------------------------------------
/android/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | net.fhtagn.libseg
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder
10 | clean,full,incremental,
11 |
12 |
13 |
14 |
15 | com.android.ide.eclipse.adt.ResourceManagerBuilder
16 |
17 |
18 |
19 |
20 | com.android.ide.eclipse.adt.PreCompilerBuilder
21 |
22 |
23 |
24 |
25 | org.eclipse.jdt.core.javabuilder
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.ApkBuilder
31 |
32 |
33 |
34 |
35 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
36 | full,incremental,
37 |
38 |
39 |
40 |
41 |
42 | com.android.ide.eclipse.adt.AndroidNature
43 | org.eclipse.jdt.core.javanature
44 | org.eclipse.cdt.core.cnature
45 | org.eclipse.cdt.core.ccnature
46 | org.eclipse.cdt.managedbuilder.core.managedBuildNature
47 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
48 |
49 |
50 |
--------------------------------------------------------------------------------
/android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/android/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 |
3 | # libseg.a
4 | include $(CLEAR_VARS)
5 | LOCAL_MODULE := libseg
6 | LOCAL_SHARED_LIBRARIES += libfigtree
7 | LOCAL_DEFAULT_CPP_EXTENSION := cc
8 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../include $(LOCAL_PATH)/../../src/third_party/miniglog/
9 | LOCAL_CPPFLAGS := -std=c++11
10 | LOCAL_CFLAGS := -D__GXX_EXPERIMENTAL_CXX0X__
11 | LOCAL_LDLIBS := -llog -ljnigraphics
12 | LOCAL_SRC_FILES := libseg.cc \
13 | ../../src/api.cc \
14 | ../../src/geodesic.cc \
15 | ../../src/kde.cc \
16 | ../../src/matting.cc \
17 | ../../src/third_party/miniglog/glog/logging.cc
18 | include $(BUILD_SHARED_LIBRARY)
19 |
20 | $(call import-module,figtree-android/jni)
21 |
--------------------------------------------------------------------------------
/android/jni/Application.mk:
--------------------------------------------------------------------------------
1 | #APP_ABI := all
2 | APP_ABI := armeabi-v7a
3 | APP_STL := gnustl_shared
4 | APP_MODULES := libseg
5 | APP_PLATFORM := android-8
6 | NDK_TOOLCHAIN_VERSION := 4.8
7 |
--------------------------------------------------------------------------------
/android/jni/libseg.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 | #include
6 | #include "api.h"
7 |
8 | extern "C" JNIEXPORT jstring JNICALL
9 | Java_net_fhtagn_libseg_SimpleMatter_hello(JNIEnv* env, jclass) {
10 | return env->NewStringUTF("Hello from JNI");
11 | }
12 |
13 | extern "C" JNIEXPORT jlong JNICALL
14 | Java_net_fhtagn_libseg_SimpleMatter_nativeNew(
15 | JNIEnv* env,
16 | jclass,
17 | jobject bitmap_image) {
18 | // TODO RGB => Lab conversion
19 | // http://www.easyrgb.com/index.php?X=MATH
20 | AndroidBitmapInfo bm_info;
21 | AndroidBitmap_getInfo(env, bitmap_image, &bm_info);
22 | CHECK_EQ(bm_info.format, ANDROID_BITMAP_FORMAT_RGBA_8888)
23 | << "Need ARGB_8888 format";
24 |
25 | char* pixels;
26 | AndroidBitmap_lockPixels(env, bitmap_image, (void**)&pixels);
27 |
28 | // TODO: SimpleMatter shouldn't make an internal copy
29 | const int W = bm_info.width;
30 | const int H = bm_info.height;
31 | std::unique_ptr r(new uint8_t[W*H]);
32 | std::unique_ptr g(new uint8_t[W*H]);
33 | std::unique_ptr b(new uint8_t[W*H]);
34 |
35 | for (int i = 0; i < H; ++i) {
36 | for (int j = 0; j < W; ++j) {
37 | r[i*W + j] = pixels[i*W*4 + j*4];
38 | g[i*W + j] = pixels[i*W*4 + j*4 + 1];
39 | b[i*W + j] = pixels[i*W*4 + j*4 + 2];
40 | }
41 | }
42 |
43 | SimpleMatter* m = new SimpleMatter(r.get(), g.get(), b.get(), W, H);
44 |
45 | AndroidBitmap_unlockPixels(env, bitmap_image);
46 |
47 | LOG(INFO) << "Created native matter : " << m;
48 | return (jlong)m;
49 | }
50 |
51 |
52 | extern "C" JNIEXPORT void JNICALL
53 | Java_net_fhtagn_libseg_SimpleMatter_nativeDestroy(
54 | JNIEnv* env,
55 | jclass,
56 | jlong obj) {
57 | SimpleMatter* m = (SimpleMatter*)obj;
58 | LOG(INFO) << "Destroying native matter : " << m;
59 | delete m;
60 | }
61 |
62 | void CheckMaskBitmap(JNIEnv* env, jobject bitmap, int expectedW, int expectedH,
63 | AndroidBitmapFormat fmt=ANDROID_BITMAP_FORMAT_RGBA_8888) {
64 | AndroidBitmapInfo bm_info;
65 | AndroidBitmap_getInfo(env, bitmap, &bm_info);
66 | CHECK_EQ(bm_info.format, fmt) << "Wrong format";
67 | CHECK_EQ(bm_info.width, expectedW);
68 | CHECK_EQ(bm_info.height, expectedH);
69 | }
70 |
71 | extern "C" JNIEXPORT void JNICALL
72 | Java_net_fhtagn_libseg_SimpleMatter_nativeUpdateMasks(
73 | JNIEnv* env,
74 | jclass,
75 | jlong obj,
76 | jobject bitmap_bgmask,
77 | jobject bitmap_fgmask) {
78 | SimpleMatter* m = (SimpleMatter*)obj;
79 | CheckMaskBitmap(env, bitmap_fgmask, m->GetWidth(), m->GetHeight());
80 | CheckMaskBitmap(env, bitmap_bgmask, m->GetWidth(), m->GetHeight());
81 |
82 | uint8_t* bg_pix;
83 | uint8_t* fg_pix;
84 | AndroidBitmap_lockPixels(env, bitmap_fgmask, (void**)&fg_pix);
85 | AndroidBitmap_lockPixels(env, bitmap_bgmask, (void**)&bg_pix);
86 |
87 | const int W = m->GetWidth();
88 | const int H = m->GetHeight();
89 |
90 | // TODO: Avoid copy
91 | std::unique_ptr fg(new uint8_t[W*H]);
92 | std::unique_ptr bg(new uint8_t[W*H]);
93 | for (int i = 0; i < H; ++i) {
94 | for (int j = 0; j < W; ++j) {
95 | // Only consider alpha
96 | fg[i*W + j] = fg_pix[i*W*4 + j*4 + 3];
97 | bg[i*W + j] = bg_pix[i*W*4 + j*4 + 3];
98 | }
99 | }
100 |
101 | m->UpdateMasks(bg.get(), fg.get());
102 |
103 | AndroidBitmap_unlockPixels(env, bitmap_bgmask);
104 | AndroidBitmap_unlockPixels(env, bitmap_fgmask);
105 | }
106 |
107 | extern "C" JNIEXPORT void JNICALL
108 | Java_net_fhtagn_libseg_SimpleMatter_nativeGetForegroundMask(
109 | JNIEnv* env,
110 | jclass,
111 | jlong obj,
112 | jobject bitmap_mask) {
113 | SimpleMatter* m = (SimpleMatter*)obj;
114 | CheckMaskBitmap(env, bitmap_mask, m->GetWidth(), m->GetHeight(),
115 | ANDROID_BITMAP_FORMAT_A_8);
116 | uint8_t* pixels;
117 | AndroidBitmap_lockPixels(env, bitmap_mask, (void**)&pixels);
118 |
119 | m->GetForegroundMask(pixels);
120 |
121 | AndroidBitmap_unlockPixels(env, bitmap_mask);
122 | }
123 |
--------------------------------------------------------------------------------
/android/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 | android.library=true
16 |
--------------------------------------------------------------------------------
/android/src/net/fhtagn/libseg/SimpleMatter.java:
--------------------------------------------------------------------------------
1 | package net.fhtagn.libseg;
2 |
3 | import android.graphics.Bitmap;
4 | import android.util.Log;
5 |
6 | // Java interface to C++ SimpleMatter
7 | public class SimpleMatter {
8 | private final static String TAG = SimpleMatter.class.getName();
9 | // The image being matted. Do NOT modify this directly
10 | public Bitmap image;
11 |
12 | // TODO: We need to handle locking somewhat
13 |
14 | // The client application should directly modify the bitmaps here and then
15 | // call updateMatter() to redo the matting
16 | public Bitmap fgScribbles;
17 | public Bitmap bgScribbles;
18 |
19 | public Bitmap finalMask;
20 |
21 | private long nativeMatter = 0;
22 |
23 | public SimpleMatter(Bitmap image) {
24 | this.image = image;
25 | this.fgScribbles = Bitmap.createBitmap(image.getWidth(),
26 | image.getHeight(),
27 | Bitmap.Config.ARGB_8888);
28 | this.bgScribbles = Bitmap.createBitmap(image.getWidth(),
29 | image.getHeight(),
30 | Bitmap.Config.ARGB_8888);
31 | this.finalMask = Bitmap.createBitmap(image.getWidth(),
32 | image.getHeight(),
33 | Bitmap.Config.ALPHA_8);
34 | Log.i(TAG, "Hello jni : " + hello());
35 | nativeMatter = nativeNew(image);
36 | }
37 |
38 | @Override
39 | protected void finalize() throws Throwable {
40 | nativeDestroy(nativeMatter);
41 | }
42 |
43 | public synchronized void updateMatter() {
44 | nativeUpdateMasks(nativeMatter, bgScribbles, fgScribbles);
45 | nativeGetForegroundMask(nativeMatter, finalMask);
46 | }
47 |
48 | // TODO: JNI methods have to be private ?
49 | static native String hello();
50 |
51 | // Create a new SimpleMatter on the C++ side, returning an identifier
52 | static native long nativeNew(Bitmap image);
53 | static native void nativeDestroy(long obj);
54 | static native void nativeUpdateMasks(long obj, Bitmap bgmask, Bitmap fgmask);
55 | // Get the foreground mask resulting from the matting
56 | static native void nativeGetForegroundMask(long obj, Bitmap mask);
57 |
58 | static {
59 | System.loadLibrary("gnustl_shared");
60 | System.loadLibrary("figtree");
61 | System.loadLibrary("seg");
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/include/api.h:
--------------------------------------------------------------------------------
1 | #ifndef _LIBMATTING_API_H_
2 | #define _LIBMATTING_API_H_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "utils.h"
9 |
10 | class Matter {
11 | public:
12 | Matter(uint8_t* lab_l, uint8_t* lab_a,
13 | uint8_t* lab_b, int W, int H);
14 | virtual ~Matter();
15 |
16 | // Fill mask with the foreground mask resulting from the matting.
17 | // 255 indicates foreground pixels, 0 background.
18 | void GetForegroundMask(uint8_t* mask);
19 |
20 | void GetForegroundLikelihood(double* out);
21 | void GetBackgroundLikelihood(double* out);
22 |
23 | void GetForegroundDist(double* out);
24 | void GetBackgroundDist(double* out);
25 |
26 | int GetWidth() { return W; }
27 | int GetHeight() { return H; }
28 |
29 | protected:
30 | int W, H;
31 | std::unique_ptr lab_l, lab_a, lab_b;
32 | // TODO: We do not actually need the pdf for each pixel of the image. Use
33 | // a simple lookup table of pixel intensity to pdf instead
34 | std::unique_ptr fg_pdf, bg_pdf;
35 | std::unique_ptr fg_likelihood, bg_likelihood;
36 | std::unique_ptr fg_dist, bg_dist;
37 | std::unique_ptr final_mask;
38 |
39 | uint8_t* channels[3];
40 | };
41 |
42 | // A simpler API that doesn't have the notion of scribbles ordering, but just
43 | // use one mask for background scribbles and one mask for foreground
44 | class SimpleMatter : public Matter {
45 | public:
46 | // The image is given in the lab colorspace. Each of lab_l, lab_a, lab_b is
47 | // a W*H array stored in row-major order
48 | // Matter makes an internal copy of the image
49 | SimpleMatter(uint8_t* lab_l, uint8_t* lab_a, uint8_t* lab_b, int W, int H);
50 | virtual ~SimpleMatter();
51 |
52 | void UpdateMasks(uint8_t* bg_mask, uint8_t* fg_mask);
53 | };
54 |
55 | // Contains the current state of the matting
56 | // This is an API that implements section 3.1.3 of [Bai09] regarding user
57 | // interaction. Basically, scribbles ordering matters and a new background
58 | // scribble will only interfer with what's current foreground and inversely.
59 | class InteractiveMatter : public Matter {
60 | public:
61 | // The image is given in the lab colorspace. Each of lab_l, lab_a, lab_b is
62 | // a W*H array stored in row-major order
63 | // Matter makes an internal copy of the image
64 | InteractiveMatter(uint8_t* lab_l, uint8_t* lab_a,
65 | uint8_t* lab_b, int W, int H);
66 | virtual ~InteractiveMatter();
67 |
68 |
69 | // Add a scribble. Note that empty scribbles (0 pixels) will be ignored
70 | //
71 | // We do not store all scribbles as a simple boolean mask because
72 | // of the way user interaction is handled (see Bai09). Scribbles ordering is
73 | // important to let user do local corrections that do not influence
74 | // the whole image. In other words, doing the matting with the same set of
75 | // scribbles but in different order might result in different result.
76 | void AddScribble(const Scribble& s);
77 |
78 | int NumScribbles() {
79 | return scribbles.size();
80 | }
81 |
82 | private:
83 | // Initially false, true when at least one scribble has been added to bg/fg
84 | bool bg_scribbled_, fg_scribbled_;
85 |
86 | std::vector scribbles;
87 | };
88 |
89 | #endif
90 |
--------------------------------------------------------------------------------
/include/geodesic.h:
--------------------------------------------------------------------------------
1 | #ifndef _GEODESIC_H_
2 | #define _GEODESIC_H_
3 |
4 | #include
5 | #include "utils.h"
6 |
7 | // Functions to compute geodesic distance between image pixels and user
8 | // scribbles as per section 3.1.2 (fig.5) of Bai09
9 |
10 | // For a W*H image (4-connected graph) given as a heightmap, compute, for each
11 | // pixel, the minimum geodesic distance to the closest source
12 | // This is described in section 3.1.2 (fig. 5) of Bai09
13 | void GeodesicDistanceMap(const std::vector& sources,
14 | const double* height,
15 | int W,
16 | int H,
17 | double* dists);
18 |
19 | void GeodesicDistanceMap(const uint8_t* source_mask,
20 | const double* height,
21 | int W,
22 | int H,
23 | double* dists);
24 |
25 | void GeodesicDistanceMap(const std::vector& scribbles,
26 | bool background,
27 | const double* height,
28 | int W,
29 | int H,
30 | double* dists);
31 |
32 | #endif
33 |
--------------------------------------------------------------------------------
/include/kde.h:
--------------------------------------------------------------------------------
1 | #ifndef _LIBMATTING_KDE_H_
2 | #define _LIBMATTING_KDE_H_
3 |
4 | // Implement Kernel Density Estimation (KDE) to estimate the color PDF
5 | // Pr(color | foreground)
6 | // This is described in section 3.1.1 (and fig.3) of Bai09. Note that like
7 | // in Bai, we perform one univariate KDE on each axis of the colorspace instead
8 | // of a multivariate 3D KDE
9 | #include
10 | #include
11 |
12 | #include "utils.h"
13 |
14 | // Use kernel density estimation to compute the probability of each point
15 | // int targets (storing the probability in target_prob) given the data points
16 | // in xis and their corresponding weights
17 | //
18 | // p = \sum_i w_i K(t - xi)
19 | //
20 | // Where K is a gaussian kernel and t is the target. w_i is the weight
21 | //
22 | // The KDE bandwidth is estimated automatically using a rule-of-thumb
23 | void UnivariateKDE(const std::vector& xis,
24 | const std::vector& weights,
25 | const std::vector& targets,
26 | std::vector* target_prob);
27 |
28 | // Uses the figtree library to compute fast KDE with the help of the
29 | // Gauss transform
30 | // epsilon is the desired maximum absolute error after normalizing output
31 | // by sum of weights.
32 | // If the weights, q_i (see below), add up to 1, then this is will be the
33 | // maximum absolute error.
34 | void FastUnivariateKDE(const std::vector& xis,
35 | const std::vector& weights,
36 | const std::vector& targets,
37 | std::vector* target_prob,
38 | double epsilon=1e-2);
39 |
40 | // Helper function to compute KDE on a single color channel for values of
41 | // x in the [0, 255] interval.
42 | // So, target_prob will have 256 entries containing the probability for each
43 | // 8bit color value.
44 | // (Optionally) A median filter is also applied to smooth the probabilities
45 | void ColorChannelKDE(const uint8_t* data,
46 | const uint8_t* mask,
47 | int W,
48 | int H,
49 | bool median_filter,
50 | std::vector* target_prob);
51 |
52 | // Same as above, but uses a list of scribbles instead of a mask. Only
53 | // scribbles with s.background == background are considered.
54 | void ColorChannelKDE(const uint8_t* data,
55 | const std::vector& scribbles,
56 | bool background,
57 | int W,
58 | int H,
59 | bool median_filter,
60 | std::vector* target_prob);
61 |
62 | void ColorChannelKDE(const std::vector& xi,
63 | bool median_filter,
64 | std::vector* target_prob);
65 |
66 | #endif
67 |
--------------------------------------------------------------------------------
/include/matting.h:
--------------------------------------------------------------------------------
1 | #ifndef _LIBMATTING_MATTING_H_
2 | #define _LIBMATTING_MATTING_H_
3 |
4 | #include
5 | #include "utils.h"
6 |
7 | // Computes P(c_x | M) where M is background/foreground estimated by KDE from
8 | // the mask. c_x is a pixel.
9 | //
10 | // outimg should be a user-allocated W*H image
11 | void ImageColorPDF(const uint8_t* const* channels,
12 | const uint8_t* mask,
13 | int W,
14 | int H,
15 | double* outimg);
16 |
17 | void ImageColorPDF(uint8_t const* const* channels,
18 | const std::vector& scribbles,
19 | bool background,
20 | int W,
21 | int H,
22 | double* outimg);
23 |
24 | void ImageColorPDF(uint8_t const* const* channels,
25 | const std::vector>& probs,
26 | int W,
27 | int H,
28 | double* outimg);
29 |
30 |
31 | // Equation 1. of Bai09 :
32 | // P_F(cx) = P(cx|F) / (P(cx|F) + P(cx|B))
33 | //
34 | // likelihood should be a user-allocated W*H image
35 | void ForegroundLikelihood(const double* P_cx_F,
36 | const double* P_cx_B,
37 | int W,
38 | int H,
39 | double* likelihood);
40 |
41 | // Compute final foreground mask
42 | // outmask is allocated by the caller as a W*H row-major array
43 | void FinalForegroundMask(const double* fg_dist,
44 | const double* bg_dist,
45 | int W,
46 | int H,
47 | uint8_t* outmask);
48 |
49 | #endif
50 |
--------------------------------------------------------------------------------
/include/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef _LIBMATTING_UTILS_H_
2 | #define _LIBMATTING_UTILS_H_
3 |
4 | #include
5 |
6 | struct Point2i {
7 | Point2i(int x, int y) : x(x), y(y) {}
8 | int x, y;
9 | };
10 |
11 | // A user-provided drawing that is either foreground or background
12 | struct Scribble {
13 | bool background;
14 | std::vector pixels;
15 | };
16 |
17 |
18 | template
19 | bool IsNaN(T t) {
20 | return t != t;
21 | }
22 |
23 | // Copy src[i] to dst[i] if mask[i] == true_val
24 | template
25 | void MaskedCopy(const T* src,
26 | const M* mask,
27 | M true_val,
28 | const int len,
29 | T* dst) {
30 | for (int i = 0; i < len; ++i) {
31 | if (mask[i] == true_val) {
32 | dst[i] = src[i];
33 | }
34 | }
35 | }
36 |
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/julienr/libseg/597b0073eb1dd0531868168654ba28e64729f8e5/screenshot.png
--------------------------------------------------------------------------------
/src/api.cc:
--------------------------------------------------------------------------------
1 | #include "api.h"
2 |
3 | #include "kde.h"
4 | #include "geodesic.h"
5 | #include "matting.h"
6 |
7 | #include
8 | #include
9 |
10 | using namespace std;
11 |
12 | Matter::Matter(uint8_t* l, uint8_t* a, uint8_t* b, int W, int H)
13 | : W(W), H(H),
14 | lab_l(new uint8_t[W*H]),
15 | lab_a(new uint8_t[W*H]),
16 | lab_b(new uint8_t[W*H]),
17 | fg_pdf(new double[W*H]),
18 | bg_pdf(new double[W*H]),
19 | fg_likelihood(new double[W*H]),
20 | bg_likelihood(new double[W*H]),
21 | fg_dist(new double[W*H]),
22 | bg_dist(new double[W*H]),
23 | final_mask(new uint8_t[W*H]) {
24 | memcpy(lab_l.get(), l, sizeof(uint8_t)*W*H);
25 | memcpy(lab_a.get(), a, sizeof(uint8_t)*W*H);
26 | memcpy(lab_b.get(), b, sizeof(uint8_t)*W*H);
27 |
28 | for (int i = 0; i < W*H; ++i) {
29 | final_mask[i] = 0;
30 | fg_dist[i] = numeric_limits::max();
31 | bg_dist[i] = numeric_limits::max();
32 | }
33 |
34 | channels[0] = lab_l.get();
35 | channels[1] = lab_a.get();
36 | channels[2] = lab_b.get();
37 | }
38 |
39 | Matter::~Matter() {}
40 |
41 | void Matter::GetForegroundLikelihood(double* out) {
42 | memcpy(out, fg_likelihood.get(), sizeof(double)*W*H);
43 | }
44 |
45 | void Matter::GetBackgroundLikelihood(double* out) {
46 | memcpy(out, bg_likelihood.get(), sizeof(double)*W*H);
47 | }
48 |
49 | void Matter::GetForegroundDist(double* out) {
50 | memcpy(out, fg_dist.get(), sizeof(double)*W*H);
51 | }
52 |
53 | void Matter::GetBackgroundDist(double* out) {
54 | memcpy(out, bg_dist.get(), sizeof(double)*W*H);
55 | }
56 |
57 | void Matter::GetForegroundMask(uint8_t* outmask) {
58 | memcpy(outmask, final_mask.get(), sizeof(uint8_t)*W*H);
59 | }
60 |
61 | SimpleMatter::SimpleMatter(uint8_t* l, uint8_t* a, uint8_t* b,
62 | int W, int H)
63 | : Matter(l, a, b, W, H) {
64 | }
65 |
66 | SimpleMatter::~SimpleMatter() {}
67 |
68 | void SimpleMatter::UpdateMasks(uint8_t* bg_mask, uint8_t* fg_mask) {
69 | // Update PDFs
70 | ImageColorPDF(channels, bg_mask, W, H, bg_pdf.get());
71 | ImageColorPDF(channels, fg_mask, W, H, fg_pdf.get());
72 |
73 | // Update likelihoods
74 | ForegroundLikelihood(fg_pdf.get(), bg_pdf.get(), H, W, fg_likelihood.get());
75 | ForegroundLikelihood(bg_pdf.get(), fg_pdf.get(), H, W, bg_likelihood.get());
76 |
77 | // Update distance maps
78 | GeodesicDistanceMap(bg_mask, bg_likelihood.get(), W, H, bg_dist.get());
79 | GeodesicDistanceMap(fg_mask, fg_likelihood.get(), W, H, fg_dist.get());
80 |
81 | // Compute final mask
82 | FinalForegroundMask(fg_dist.get(), bg_dist.get(), W, H, final_mask.get());
83 | }
84 |
85 | InteractiveMatter::InteractiveMatter(uint8_t* l, uint8_t* a, uint8_t* b,
86 | int W, int H)
87 | : Matter(l, a, b, W, H),
88 | bg_scribbled_(false),
89 | fg_scribbled_(false) {
90 | }
91 |
92 | InteractiveMatter::~InteractiveMatter() {}
93 |
94 | void InteractiveMatter::AddScribble(const Scribble& s) {
95 | if (s.pixels.size() == 0) {
96 | LOG(WARNING) << "Ignoring empty scribble";
97 | return;
98 | }
99 | scribbles.push_back(s);
100 |
101 | // 1. Update bg or fg pdf (depending on scribble's background attribute)
102 | if (s.background) {
103 | //cout << "Updating bg pdf" << endl;
104 | ImageColorPDF(channels, scribbles, true, W, H, bg_pdf.get());
105 | } else {
106 | //cout << "Updating fg pdf" << endl;
107 | ImageColorPDF(channels, scribbles, false, W, H, fg_pdf.get());
108 | }
109 |
110 | // 2. Update fg AND bg likelihood
111 | ForegroundLikelihood(fg_pdf.get(), bg_pdf.get(), H, W, fg_likelihood.get());
112 | ForegroundLikelihood(bg_pdf.get(), fg_pdf.get(), H, W, bg_likelihood.get());
113 |
114 | #if 1
115 | // 3. Update fg or bg distance map, but only for pixels within the
116 | // fg (if bg scribble) or bg (if fg scribble).
117 | unique_ptr newdist(new double[W*H]);
118 | if (s.background) {
119 | GeodesicDistanceMap(scribbles, true, bg_likelihood.get(), W, H,
120 | newdist.get());
121 | if (!bg_scribbled_) { // special case for first scribble
122 | memcpy(bg_dist.get(), newdist.get(), sizeof(double)*W*H);
123 | bg_scribbled_ = true;
124 | } else {
125 | MaskedCopy(newdist.get(), final_mask.get(), 255,
126 | W*H, bg_dist.get());
127 | }
128 | } else {
129 | GeodesicDistanceMap(scribbles, false, fg_likelihood.get(), W, H,
130 | newdist.get());
131 | if (!fg_scribbled_) { // special case for first scribble
132 | memcpy(fg_dist.get(), newdist.get(), sizeof(double)*W*H);
133 | fg_scribbled_ = true;
134 | } else {
135 | MaskedCopy(newdist.get(), final_mask.get(), 0,
136 | W*H, fg_dist.get());
137 |
138 | }
139 | }
140 | #else
141 | // 3. (alternative) Update everything
142 | GeodesicDistanceMap(scribbles, true, bg_likelihood.get(), W, H,
143 | bg_dist.get());
144 | GeodesicDistanceMap(scribbles, false, fg_likelihood.get(), W, H,
145 | fg_dist.get());
146 | #endif
147 |
148 | // 4. Compute final mask
149 | FinalForegroundMask(fg_dist.get(), bg_dist.get(), W, H, final_mask.get());
150 | }
151 |
--------------------------------------------------------------------------------
/src/geodesic.cc:
--------------------------------------------------------------------------------
1 | #include "geodesic.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | using namespace std;
11 |
12 | void GeodesicDistanceMap(const uint8_t* source_mask,
13 | const double* height,
14 | int W, int H,
15 | double* dists) {
16 | vector points;
17 | for (int x = 0; x < W; ++x) {
18 | for (int y = 0; y < H; ++y) {
19 | if (source_mask[W*y + x]) {
20 | points.push_back(Point2i(x, y));
21 | }
22 | }
23 | }
24 | GeodesicDistanceMap(points, height, W, H, dists);
25 | }
26 |
27 | void GeodesicDistanceMap(const std::vector& scribbles,
28 | bool background,
29 | const double* height,
30 | int W, int H,
31 | double* dists) {
32 | vector points;
33 | for (const Scribble& s : scribbles) {
34 | if (s.background == background) {
35 | points.insert(points.end(), s.pixels.begin(), s.pixels.end());
36 | }
37 | }
38 | GeodesicDistanceMap(points, height, W, H, dists);
39 | }
40 |
41 | void GeodesicDistanceMap(const std::vector& sources,
42 | const double* height,
43 | int W,
44 | int H,
45 | double* dists) {
46 | // The algorithm is actually equivalent to running Dijkstra once for each
47 | // source and then keeping the minimum distance.
48 | // This is similar to "SHORTEST-PATH FOREST WITH TOPOLOGICAL ORDERING"
49 | //
50 | // But we do it all at once so it should be faster. It works as follow :
51 | // 1. assign a distance of 0 to all source nodes, infinity to others
52 | // 2. create a list of unvisited nodes consisting of the source nodes
53 | // 3. visit neighbors of the current node
54 | // - if the current node allows a shortest path to the neighbor
55 | // - update the neighbor dist
56 | // - add the neighbor to the unvisited nodes
57 | // 4. remove current node from visited
58 | // 5. pick the node with the smallest distance from the unvisited node as
59 | // the new current
60 | const int N = W*H;
61 | // priority queue
62 | typedef pair PriorityEntry;
63 | auto comp = [](const PriorityEntry& e1, const PriorityEntry& e2) {
64 | return e1.second > e2.second;
65 | };
66 | priority_queue, decltype(comp)> Q(comp);
67 |
68 | for (int i = 0; i < N; ++i) {
69 | dists[i] = numeric_limits::max();
70 | }
71 |
72 | for (const Point2i& p : sources) {
73 | const int i = W*p.y + p.x;
74 | dists[i] = 0;
75 | Q.push(make_pair(i, 0));
76 | }
77 |
78 | //dx dy pairs for neighborhood exploration
79 | const int dx[4] = {-1, 0, 1, 0};
80 | const int dy[4] = { 0, 1, 0, -1};
81 |
82 | // main loop
83 | while(!Q.empty()) {
84 | int u = Q.top().first;
85 | const int ux = u % W;
86 | const int uy = u / W;
87 | Q.pop();
88 | // explore neighbors
89 | for (int i = 0; i < 4; ++i) {
90 | const int vx = ux + dx[i];
91 | const int vy = uy + dy[i];
92 | if ((vx < 0 || vx >= W) || (vy < 0 || vy >= H)) {
93 | continue;
94 | }
95 | const int v = vy*W + vx;
96 | const double w = fabs(height[v] - height[u]);
97 |
98 | if ((dists[u] + w) < dists[v]) { // we found a shortest path to v
99 | dists[v] = dists[u] + w;
100 | // TODO: should UPDATE existing v (instead of duplicating)
101 | Q.push(make_pair(v, dists[v]));
102 | }
103 | }
104 | }
105 | }
106 |
107 |
--------------------------------------------------------------------------------
/src/geodesic_test.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "geodesic.h"
6 |
7 | using namespace std;
8 | using ::testing::DoubleNear;
9 |
10 | namespace {
11 |
12 | TEST(GeodesicDistanceMap, Simple) {
13 | double height[] = {
14 | 0, 1, 2, 1,
15 | 0, 2, 1, 2,
16 | 0, 1, 0, 1
17 | };
18 | uint8_t sources[] = {
19 | 1, 0, 0, 0,
20 | 0, 0, 0, 0,
21 | 0, 0, 1, 0
22 | };
23 | const int W = 4;
24 | const int H = 3;
25 | double dists[W*H];
26 | GeodesicDistanceMap(sources, height, W, H, dists);
27 |
28 | uint8_t expected_dists[] = {
29 | 0, 1, 2, 3,
30 | 0, 2, 1, 2,
31 | 0, 1, 0, 1,
32 | };
33 |
34 | //cout << "dists" << endl;
35 | for (int i = 0; i < 3; ++i) {
36 | for (int j = 0; j < 4; ++j) {
37 | ASSERT_EQ(dists[i*W + j], expected_dists[i*W + j])
38 | << "difference at (" << i << ", " << j << ") : " << dists[i*W+j]
39 | << " != " << expected_dists[i*W + j];
40 | //cout << "\t" << dists[i*4 + j];
41 | }
42 | //cout << endl;
43 | }
44 | }
45 |
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/kde.cc:
--------------------------------------------------------------------------------
1 | #include "kde.h"
2 | #include
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | #include
9 |
10 | using namespace std;
11 |
12 | const double NORMAL_FACTOR = 1/(double)sqrt(2*M_PI);
13 |
14 | double GaussianKernel(double t, double xi, double h, bool verbose=false) {
15 | const double x = (t - xi) / h;
16 | // TODO: We have to remove NORMAL_FACTOR so that is sums to 1
17 | // (see plot_densities.py). Is that normal ?
18 | if (verbose) {
19 | LOG(INFO) << "xi : " << xi << ", t : " << t << ", h : " << h
20 | << " => x = " << x << " => x*x = " << x*x;
21 | }
22 |
23 | //return NORMAL_FACTOR * exp(-0.5 * x * x);
24 | return exp(-0.5 * x * x);
25 | }
26 |
27 | // Use Scott's Rule, like scipy :
28 | // h = n**(-1./(d+4))
29 | // where n is the number of data points, d the number of dimensions
30 | // http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.gaussian_kde.html
31 | double EstimateBandwidth(int ndata, int ndims) {
32 | //return pow(ndata, -1.0/(double)(ndims + 4));
33 | // TODO: Hardcoded 0.1 value works better than estimator
34 | return 0.1;
35 | }
36 |
37 | void UnivariateKDE(const vector& xis,
38 | const vector& weights,
39 | const vector& targets,
40 | vector* target_prob) {
41 | CHECK_EQ(xis.size(), weights.size());
42 | const double h = EstimateBandwidth(xis.size(), 1);
43 | //LOG(INFO) << "h : " << h;
44 | for (size_t ti = 0; ti < targets.size(); ++ti) {
45 | double prob = 0;
46 | for (size_t i = 0; i < xis.size(); ++i) {
47 | prob += weights[i] * GaussianKernel(targets[ti], xis[i], h);
48 | }
49 | //if (prob == 0) {
50 | //for (size_t i = 0; i < xis.size(); ++i) {
51 | //LOG(INFO) << "i : " << i << ", " << weights[i] << ", "
52 | //<< GaussianKernel(targets[ti], xis[i], h, true);
53 | //}
54 | //}
55 | target_prob->push_back(prob);
56 | }
57 | }
58 |
59 | void FastUnivariateKDE(const std::vector& xis,
60 | const std::vector& weights,
61 | const std::vector& targets,
62 | std::vector* target_prob,
63 | double epsilon) {
64 | // libfigtree doesn't like empty xis
65 | if (xis.size() == 0) {
66 | // Uniform distribution
67 | target_prob->resize(targets.size(), 1.0/(double)targets.size());
68 | return;
69 | }
70 |
71 | // Figtree's h is not exactly the same as standard deviation :
72 | // (from figtree sample.cpp)
73 | // The bandwidth. NOTE: this is not the same as standard deviation since
74 | // the Gauss Transform sums terms exp( -||x_i - y_j||^2 / h^2 ) as opposed
75 | // to exp( -||x_i - y_j||^2 / (2*sigma^2) ). Thus, if sigma is known,
76 | // bandwidth can be set to h = sqrt(2)*sigma.
77 | const double h = sqrt(2) * EstimateBandwidth(xis.size(), 1);
78 | //const double h = sqrt(2) * 30;
79 | //LOG(INFO) << "h : " << h;
80 | const int d = 1;
81 | const int N = xis.size();
82 | const int M = targets.size();
83 | const int W = 1;
84 |
85 | target_prob->resize(M);
86 |
87 | figtree(d, N, M, W,
88 | const_cast(xis.data()),
89 | h,
90 | const_cast(weights.data()),
91 | const_cast(targets.data()),
92 | epsilon,
93 | target_prob->data());
94 | }
95 |
96 | // Return the median of the elements in v. Note that v WILL be modified
97 | template
98 | T Median(vector* v) {
99 | // TODO: This is not completely correct. If v has an even number of elements,
100 | // the median should be the average of the middle two
101 | // http://stackoverflow.com/questions/1719070/what-is-the-right-approach-when-using-stl-container-for-median-calculation
102 | size_t n = v->size() / 2;
103 | std::nth_element(v->begin(), v->begin() + n, v->end());
104 | return v->at(n);
105 | }
106 |
107 | void MedianFilter(const vector& v,
108 | size_t hwsize, // half window size
109 | vector* vfilt) {
110 | for (size_t i = 0; i < v.size(); ++i) {
111 | const int wstart = max(0, i - hwsize);
112 | const int wend = min(v.size() - 1, i + hwsize);
113 | vector window(v.begin() + wstart, v.begin() + wend);
114 | vfilt->push_back(Median(&window));
115 | }
116 | }
117 |
118 | void ColorChannelKDE(const uint8_t* data,
119 | const uint8_t* mask,
120 | int W,
121 | int H,
122 | bool median_filter,
123 | std::vector* target_prob) {
124 | vector xis;
125 | for (int i = 0; i < W*H; ++i) {
126 | if (mask[i]) {
127 | xis.push_back(data[i]);
128 | }
129 | }
130 |
131 | ColorChannelKDE(xis, median_filter, target_prob);
132 | }
133 |
134 | void ColorChannelKDE(const uint8_t* data,
135 | const vector& scribbles,
136 | bool background,
137 | int W,
138 | int H,
139 | bool median_filter,
140 | std::vector* target_prob) {
141 | vector xis;
142 | for (const Scribble& s : scribbles) {
143 | if (s.background == background) {
144 | for (const Point2i& p : s.pixels) {
145 | xis.push_back(data[W*p.y + p.x]);
146 | }
147 | }
148 | }
149 | ColorChannelKDE(xis, median_filter, target_prob);
150 | }
151 |
152 | void ColorChannelKDE(const std::vector& xis,
153 | bool median_filter,
154 | std::vector* target_prob) {
155 | vector weights(xis.size(), 1/(double)xis.size());
156 | vector targets;
157 | //for (int i = 0; i < 255; ++i) {
158 | //targets.push_back(i);
159 | //}
160 |
161 | // z-score normalize xis and targets to the [-1,1] range
162 | // Otherwise, the standard gaussian kernel (e^(-0.5*(x-t)**2)) will blow up
163 | // because x-t will be too large
164 | vector nx(xis.size(), 0);
165 | for (size_t i = 0; i < xis.size(); ++i) {
166 | nx[i] = (xis[i] - 128) / 128.0;
167 | }
168 | for (int i = 0; i < 255; ++i) {
169 | targets.push_back((i - 128)/128.0);
170 | }
171 | //UnivariateKDE(nx, weights, targets, target_prob);
172 | FastUnivariateKDE(nx, weights, targets, target_prob);
173 |
174 | // TODO: Median filtering is useless (look at plot_densities)
175 | if (median_filter) {
176 | vector medfilt;
177 | MedianFilter(*target_prob, 5, &medfilt);
178 | *target_prob = medfilt;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/kde_test.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "kde.h"
6 |
7 | using namespace std;
8 | using ::testing::DoubleNear;
9 |
10 | namespace {
11 |
12 | TEST(FastUnivariateKDE, FastSlow) {
13 | // Ensure UnivariateKDE and FastUnivariateKDE produce the same results
14 |
15 | // Source
16 | vector x{0.7165, 0.5113, 0.7764, 0.4893, 0.1859};
17 |
18 | // source weights
19 | vector w{0.2280, 0.4496, 0.1722, 0.9688, 0.3557};
20 |
21 | // Targets
22 | vector y{0.9561, 0.5955, 0.0287, 0.8121, 0.6101, 0.7015, 0.0922,
23 | 0.4249, 0.3756, 0.1662, 0.8332, 0.8386, 0.4516, 0.9566,
24 | 0.1472, 0.8699, 0.7694, 0.4442, 0.6206};
25 |
26 | const double epsilon = 1e-2;
27 |
28 | vector slow_prob;
29 | vector fast_prob;
30 | UnivariateKDE(x, w, y, &slow_prob);
31 | FastUnivariateKDE(x, w, y, &fast_prob, epsilon);
32 |
33 | ASSERT_EQ(slow_prob.size(), fast_prob.size());
34 | for (size_t i = 0; i < slow_prob.size(); ++i) {
35 | ASSERT_THAT(slow_prob[i], DoubleNear(fast_prob[i], 1e-2))
36 | << "At index " << i << " : " << slow_prob[i] << " != " << fast_prob[i];
37 | }
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/matting.cc:
--------------------------------------------------------------------------------
1 | #include "matting.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "kde.h"
10 | #include "geodesic.h"
11 |
12 | using namespace std;
13 |
14 | void ImageColorPDF(uint8_t const* const* channels,
15 | const uint8_t* mask,
16 | int W,
17 | int H,
18 | double* outimg) {
19 | vector> probs(3);
20 | for (int i = 0; i < 3; ++i) {
21 | ColorChannelKDE(channels[i], mask, W, H, true, &probs[i]);
22 | }
23 |
24 | ImageColorPDF(channels, probs, W, H, outimg);
25 | }
26 |
27 | void ImageColorPDF(uint8_t const* const* channels,
28 | const std::vector& scribbles,
29 | bool background,
30 | int W,
31 | int H,
32 | double* outimg) {
33 | vector> probs(3);
34 | for (int i = 0; i < 3; ++i) {
35 | ColorChannelKDE(channels[i], scribbles, background, W, H, true, &probs[i]);
36 | }
37 |
38 | ImageColorPDF(channels, probs, W, H, outimg);
39 | }
40 |
41 | void ImageColorPDF(uint8_t const* const* channels,
42 | const vector>& probs,
43 | int W,
44 | int H,
45 | double* outimg) {
46 | double prob_max = numeric_limits::min();
47 | for (int i = 0; i < W*H; ++i) {
48 | // The probabilities at a given value are very low (< 0.030), which is
49 | // mathematically correct (we have 255 such values and they sum to 1), but
50 | // can be problematic numerically. So we scale them by a factor of 10
51 | // TODO: Check that has no other incidence
52 | const double prob = probs[0][channels[0][i]]
53 | * probs[1][channels[1][i]]
54 | * probs[2][channels[2][i]];
55 | outimg[i] = prob;
56 | if (prob > prob_max) {
57 | prob_max = prob;
58 | }
59 | }
60 | }
61 |
62 | void ForegroundLikelihood(const double* P_cx_F,
63 | const double* P_cx_B,
64 | int W,
65 | int H,
66 | double* likelihood) {
67 | for (int i = 0; i < W*H; ++i) {
68 | // Avoid division by zero
69 | if (P_cx_F[i] == 0 && P_cx_B[i] == 0) {
70 | // If P(cx|B) = 0, we have P(cx|F) / (P(cx|F), so set to 1
71 | likelihood[i] = 1;
72 | } else {
73 | likelihood[i] = P_cx_F[i] / (P_cx_F[i] + P_cx_B[i]);
74 | }
75 | }
76 | }
77 |
78 | void FinalForegroundMask(const double* fg_dist,
79 | const double* bg_dist,
80 | int W,
81 | int H,
82 | uint8_t* outmask) {
83 | const int N = W*H;
84 | for (int i = 0; i < N; ++i) {
85 | outmask[i] = (fg_dist[i] < bg_dist[i]) ? 255 : 0;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/third_party/miniglog/README.rst:
--------------------------------------------------------------------------------
1 | This is the miniglog (a minimal version of Google logging library) library from ceres-solver :
2 | https://code.google.com/p/ceres-solver/source/browse/internal/ceres/miniglog/#miniglog%2Fglog
3 |
4 | This is BSD licensed.
5 |
--------------------------------------------------------------------------------
/src/third_party/miniglog/glog/logging.cc:
--------------------------------------------------------------------------------
1 | // Ceres Solver - A fast non-linear least squares minimizer
2 | // Copyright 2012 Google Inc. All rights reserved.
3 | // http://code.google.com/p/ceres-solver/
4 | //
5 | // Redistribution and use in source and binary forms, with or without
6 | // modification, are permitted provided that the following conditions are met:
7 | //
8 | // * Redistributions of source code must retain the above copyright notice,
9 | // this list of conditions and the following disclaimer.
10 | // * Redistributions in binary form must reproduce the above copyright notice,
11 | // this list of conditions and the following disclaimer in the documentation
12 | // and/or other materials provided with the distribution.
13 | // * Neither the name of Google Inc. nor the names of its contributors may be
14 | // used to endorse or promote products derived from this software without
15 | // specific prior written permission.
16 | //
17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 | // POSSIBILITY OF SUCH DAMAGE.
28 | //
29 | // Author: keir@google.com (Keir Mierle)
30 |
31 | #include "glog/logging.h"
32 |
33 | namespace google {
34 |
35 | // This is the set of log sinks. This must be in a separate library to ensure
36 | // that there is only one instance of this across the entire program.
37 | std::set log_sinks_global;
38 |
39 | } // namespace ceres
40 |
--------------------------------------------------------------------------------
/unix/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | CCACHE_SLOPPINESS=time_macros ninja -C out/Default $@
3 |
--------------------------------------------------------------------------------
/unix/clean.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | CCACHE_SLOPPINESS=time_macros ninja -C out/Default -t clean
3 |
--------------------------------------------------------------------------------
/unix/gdb.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | LIB_PATH=out/Default/lib:$FIGTREE/unix/:third_party/_install/lib:$LD_LIBRARY_PATH
3 | DYLD_LIBRARY_PATH=$LIB_PATH LD_LIBRARY_PATH=$LIB_PATH gdb --ex run --args $@
4 |
--------------------------------------------------------------------------------
/unix/gen_ninja.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #COMP=g++
3 | #COMP="clang++"
4 | COMP="ccache g++"
5 | export CXX=$COMP
6 | export CC=$COMP
7 |
8 | gyp project.gyp --depth=. -f ninja -G output_dir=$PWD/out -D ROOTDIR=${PWD} \
9 | -D SRCDIR=../src -D INCDIR=../include -D FIGTREE=$FIGTREE
10 |
--------------------------------------------------------------------------------
/unix/plot_densities.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pylab as pl
3 |
4 | #densities = np.genfromtxt('densities.txt')
5 | #print densities.shape
6 |
7 | for densfile in ['densities_nomedfilter.txt', 'densities_medfilter.txt']:
8 | densities = np.genfromtxt(densfile)
9 | pl.figure()
10 | pl.suptitle(densfile)
11 | for c in xrange(3):
12 | print '--- Channel %d' % c
13 | pl.subplot(3, 1, c + 1)
14 | pl.title('Channel %d' % c)
15 | p_fg = densities[2*c]
16 | p_bg = densities[2*c + 1]
17 | print "count(p_fg == 0) : ", np.sum(p_fg == 0)
18 | print "count(p_bg == 0) : ", np.sum(p_bg == 0)
19 | pl.plot(np.arange(255), p_fg, c='b', label='fg')
20 | pl.plot(np.arange(255), p_bg, c='g', label='bg')
21 | pl.legend()
22 | print 'sum(p_fg) : ', np.sum(p_fg)
23 | print 'sum(p_bg) : ', np.sum(p_bg)
24 | pl.show()
25 |
--------------------------------------------------------------------------------
/unix/project.gyp:
--------------------------------------------------------------------------------
1 | # gyp --toplevel-dir option doesn't seem to work as we want when the .gyp
2 | # file is also out of the source tree.
3 | # So instead, use SRCDIR environment variable set by gen_ninja.sh to locate
4 | # source files
5 | {
6 | 'variables': {
7 | 'pkg-config' : 'pkg-config',
8 |
9 | },
10 |
11 | 'target_defaults': {
12 | 'cflags':[
13 | '-Wall', '-Wextra', '-Wno-unused-parameter', '-Werror', '-std=c++11',
14 | '-Wno-unused-variable',
15 | '-Wno-unused-but-set-variable',
16 | #'-O2',
17 | '-ggdb',
18 | ],
19 | 'include_dirs': [
20 | '<(INCDIR)',
21 | ]
22 | },
23 |
24 | 'targets': [
25 | {
26 | 'target_name' : 'miniglog',
27 | 'type': 'static_library',
28 | 'cflags': [],
29 | 'sources': [
30 | '<(SRCDIR)/third_party/miniglog/glog/logging.cc'
31 | ],
32 | 'include_dirs': [
33 | '<(SRCDIR)/third_party/miniglog/'
34 | ],
35 | 'direct_dependent_settings': {
36 | 'include_dirs': [
37 | '<(SRCDIR)/third_party/miniglog/',
38 | ],
39 | }
40 | },
41 | {
42 | # See gmock README
43 | 'target_name' : 'gtest_mock',
44 | 'type': 'static_library',
45 | 'cflags': [
46 | '-Wno-missing-field-initializers',
47 | '-isystem <(ROOTDIR)/third_party/gmock-1.7.0/include',
48 | '-isystem <(ROOTDIR)/third_party/gmock-1.7.0/gtest/include',
49 | ],
50 | 'sources':[
51 | 'third_party/gmock-1.7.0/src/gmock-all.cc',
52 | 'third_party/gmock-1.7.0/src/gmock_main.cc',
53 | 'third_party/gmock-1.7.0/gtest/src/gtest-all.cc',
54 | ],
55 | 'include_dirs': [
56 | 'third_party/gmock-1.7.0/',
57 | 'third_party/gmock-1.7.0/gtest/',
58 | ],
59 | 'libraries': [
60 | '-pthread',
61 | ],
62 | 'direct_dependent_settings': {
63 | 'include_dirs': [
64 | 'third_party/gmock-1.7.0/include',
65 | 'third_party/gmock-1.7.0/gtest/include',
66 | ],
67 | 'libraries': [
68 | '-lpthread',
69 | ]
70 | },
71 | },
72 | {
73 | 'target_name' : 'libmatting',
74 | 'type': 'static_library',
75 | 'sources':[
76 | '<(SRCDIR)/api.cc',
77 | '<(SRCDIR)/kde.cc',
78 | '<(SRCDIR)/geodesic.cc',
79 | '<(SRCDIR)/matting.cc',
80 | ],
81 | 'include_dirs':[
82 | '<(FIGTREE)/include/figtree/',
83 | ],
84 | 'direct_dependent_settings': {
85 | 'libraries': [
86 | '-L<(FIGTREE)/unix/',
87 | '-lfigtree',
88 | ]
89 | },
90 | 'export_dependent_settings': [
91 | 'miniglog',
92 | ],
93 | 'dependencies' : [
94 | 'miniglog',
95 | ]
96 | },
97 | {
98 | 'target_name' : 'main',
99 | 'type' : 'executable',
100 | 'sources':[
101 | 'samples/cvutils.cc',
102 | 'samples/main.cc',
103 | ],
104 | 'include_dirs': [
105 | 'samples',
106 | ],
107 | 'libraries':[
108 | '
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "utils.h"
14 |
15 | void WaitForEsc();
16 |
17 | void ShowImage(const cv::Mat& img,
18 | const std::string& window_name,
19 | bool wait_for_esc=true);
20 |
21 | // Similar to matlab's imagesc
22 | template
23 | void ImageSC(const cv::Mat& img,
24 | const std::string& window_name,
25 | bool wait_for_esc=true,
26 | bool verbose=true,
27 | float Amin=NAN,
28 | float Amax=NAN,
29 | bool colormap=true) {
30 | using namespace std;
31 | if (IsNaN(Amin) || IsNaN(Amax)) {
32 | Amin = *min_element(img.begin(), img.end());
33 | Amax = *max_element(img.begin(), img.end());
34 | }
35 |
36 | if (verbose) {
37 | cout << "[ImageSC " << window_name << "] min = " << Amin
38 | << ", max = " << Amax << endl;
39 | }
40 | cv::Mat A_scaled = (img - Amin)/(Amax - Amin);
41 | //LOG(INFO) << "A_scaled max : " << *max_element(A_scaled.begin(), A_scaled.end());
42 | cv::Mat display;
43 | A_scaled.convertTo(display, CV_8UC1, 255.0, 0);
44 | if (colormap) {
45 | cv::applyColorMap(display, display, cv::COLORMAP_JET);
46 | }
47 |
48 | ShowImage(display, window_name, wait_for_esc);
49 | }
50 |
51 |
52 |
53 | #endif
54 |
--------------------------------------------------------------------------------
/unix/samples/interactive.cc:
--------------------------------------------------------------------------------
1 | // InteractiveMatter demo
2 | #include
3 |
4 | #include
5 |
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | #include "cvutils.h"
12 |
13 | #include "api.h"
14 |
15 | using boost::scoped_ptr;
16 | using boost::scoped_array;
17 | using namespace std;
18 | using namespace cv;
19 | using namespace std::chrono;
20 |
21 | Mat fg_layer, bg_layer;
22 | enum DrawMode {
23 | DRAW_BG,
24 | DRAW_FG
25 | };
26 | DrawMode draw_mode = DRAW_BG;
27 | bool drawing = false;
28 | scoped_ptr current_scribble;
29 | scoped_ptr matter;
30 |
31 | // Used to track cursor movements between two MOUSEMOVE events. For example
32 | // on OSX, it seems the MOUSEMOVE events have a somewhat important interval
33 | // between them
34 | int x_prev = -1, y_prev = -1;
35 |
36 | const int SCRIBLE_RADIUS = 5;
37 |
38 | static void DrawScribbleCircle(int x, int y) {
39 | const int radius = SCRIBLE_RADIUS;
40 | const int rr = radius*radius;
41 | for (int dx = -radius; dx <=radius; ++dx) {
42 | for (int dy = -radius; dy <=radius; ++dy) {
43 | if ((dx*dx + dy*dy) <= rr) {
44 | current_scribble->pixels.push_back(::Point2i(x + dx, y + dy));
45 | if (draw_mode == DRAW_FG) {
46 | fg_layer.at(y + dy, x + dx) = 255;
47 | } else {
48 | bg_layer.at(y + dy, x + dx) = 255;
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | static float Length(const int* vec) {
56 | return sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
57 | }
58 |
59 | static void OnMouse(int event, int x, int y, int, void*) {
60 | if (event == EVENT_LBUTTONDOWN) {
61 | drawing = true;
62 | draw_mode = DRAW_FG;
63 | current_scribble.reset(new Scribble);
64 | current_scribble->background = false;
65 | x_prev = x;
66 | y_prev = y;
67 | } else if (event == EVENT_RBUTTONDOWN) {
68 | drawing = true;
69 | draw_mode = DRAW_BG;
70 | current_scribble.reset(new Scribble);
71 | current_scribble->background = true;
72 | x_prev = x;
73 | y_prev = y;
74 | } else if (event == EVENT_MOUSEMOVE && drawing) {
75 | if (x_prev != -1 && y_prev != -1) {
76 | // Draw scribbles on a line between (x_prev, y_prev) and (x,y), spaced
77 | // by SCRIBBLE_RADIUS
78 | const int d[2] = { x - x_prev, y - y_prev };
79 | const float len = Length(d);
80 | const float nsteps = len / (float)SCRIBLE_RADIUS;
81 | if (nsteps > 0) {
82 | const float tstep = 1.0f / nsteps;
83 | for (float t = 0; t < 1.0f; t += tstep) {
84 | const int curr_x = (int)(x_prev + t * d[0]);
85 | const int curr_y = (int)(y_prev + t * d[1]);
86 | DrawScribbleCircle(curr_x, curr_y);
87 | }
88 | }
89 | x_prev = x;
90 | y_prev = y;
91 | }
92 | } else if (event == EVENT_LBUTTONUP || event == EVENT_RBUTTONUP) {
93 | if (current_scribble->pixels.size() > 0) {
94 | LOG(INFO) << "-- Adding scribble";
95 | matter->AddScribble(*current_scribble);
96 | LOG(INFO) << "-- done";
97 | } else {
98 | LOG(INFO) << "-- Ignoring empty scribble";
99 | }
100 | current_scribble.reset();
101 | drawing = false;
102 | x_prev = -1;
103 | y_prev = -1;
104 | }
105 | }
106 |
107 | int main(int argc, char** argv) {
108 | string imgname = "data/default_img.jpg";
109 | if (argc > 1) {
110 | imgname = argv[1];
111 | }
112 |
113 | Mat img = imread(imgname, CV_LOAD_IMAGE_COLOR);
114 | CHECK(img.data);
115 |
116 | fg_layer = Mat::zeros(img.rows, img.cols, CV_8UC1);
117 | bg_layer = Mat::zeros(img.rows, img.cols, CV_8UC1);
118 |
119 | namedWindow("img", 0);
120 | setMouseCallback("img", OnMouse, 0);
121 | imshow("img", img);
122 |
123 | //namedWindow("matted", 0);
124 |
125 | cout << "Instructions : " << endl;
126 | cout << "- left click to draw foreground scribbles" << endl;
127 | cout << "- right click to draw background scribbles" << endl;
128 |
129 | const int W = img.cols;
130 | const int H = img.rows;
131 |
132 | // RGB -> lab
133 | cv::Mat img_lab;
134 | cv::cvtColor(img, img_lab, CV_BGR2Lab);
135 | vector lab;
136 | scoped_array lab_l(new uint8_t[W*H]);
137 | lab.push_back(cv::Mat(H, W, CV_8U, lab_l.get()));
138 | scoped_array lab_a(new uint8_t[W*H]);
139 | lab.push_back(cv::Mat(H, W, CV_8U, lab_a.get()));
140 | scoped_array lab_b(new uint8_t[W*H]);
141 | lab.push_back(cv::Mat(H, W, CV_8U, lab_b.get()));
142 | cv::split(img_lab, lab);
143 |
144 | matter.reset(new InteractiveMatter(lab_l.get(), lab_a.get(), lab_b.get(),
145 | W, H));
146 |
147 | scoped_array fg_likelihood(new double[W*H]);
148 | cv::Mat fg_likelihood_mat(H, W, CV_64F, fg_likelihood.get());
149 | fg_likelihood_mat.setTo(0);
150 | scoped_array bg_likelihood(new double[W*H]);
151 | cv::Mat bg_likelihood_mat(H, W, CV_64F, bg_likelihood.get());
152 | bg_likelihood_mat.setTo(0);
153 |
154 | scoped_array fg_dist(new double[W*H]);
155 | Mat fg_dist_mat(H, W, CV_64F, fg_dist.get());
156 | fg_dist_mat.setTo(0);
157 | scoped_array bg_dist(new double[W*H]);
158 | Mat bg_dist_mat(H, W, CV_64F, bg_dist.get());
159 | bg_dist_mat.setTo(0);
160 |
161 | scoped_array final_mask(new uint8_t[W*H]);
162 | cv::Mat final_mask_mat(H, W, CV_8UC1, final_mask.get());
163 | final_mask_mat.setTo(0);
164 |
165 | int nscribbles = matter->NumScribbles();
166 |
167 | Mat result(img.rows, img.cols, img.type(), Scalar::all(0));
168 |
169 | while (true) {
170 | auto start = high_resolution_clock::now();
171 | // http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_core/py_image_arithmetics/py_image_arithmetics.html
172 | // Display scribbles on top of images :
173 | // - fg scribbles are green
174 | // - bg scribbles are red
175 | // - fg and bg scribbles intersections (bad) are pink
176 | Mat disp_img;
177 | img.copyTo(disp_img);
178 | CHECK_EQ(disp_img.type(), CV_8UC3);
179 |
180 | disp_img.setTo(Scalar(0, 255, 0), fg_layer);
181 | disp_img.setTo(Scalar(0, 0, 255), bg_layer);
182 | {
183 | Mat layer_overlap;
184 | bitwise_and(fg_layer, bg_layer, layer_overlap);
185 | disp_img.setTo(Scalar(255, 105, 180), layer_overlap);
186 | }
187 | imshow("img", disp_img);
188 |
189 | // Refresh images from matter if something has changed
190 | if (matter->NumScribbles() != nscribbles) {
191 | LOG(INFO) << "Refreshing from matter";
192 | matter->GetForegroundLikelihood(fg_likelihood.get());
193 | matter->GetBackgroundLikelihood(bg_likelihood.get());
194 | matter->GetForegroundDist(fg_dist.get());
195 | matter->GetBackgroundDist(bg_dist.get());
196 | matter->GetForegroundMask(final_mask.get());
197 | nscribbles = matter->NumScribbles();
198 | }
199 | ImageSC(fg_likelihood_mat, "fg_likelihood", false, false);
200 | ImageSC(bg_likelihood_mat, "bg_likelihood", false, false);
201 |
202 | ImageSC(fg_dist_mat, "fg_dist", false, false);
203 | ImageSC(bg_dist_mat, "bg_dist", false, false);
204 |
205 | ImageSC(final_mask_mat, "final_mask", false, false);
206 |
207 | result.setTo(0);
208 | img.copyTo(result, final_mask_mat);
209 | ShowImage(result, "result", false);
210 |
211 | auto end = high_resolution_clock::now();
212 | //LOG(INFO) << "time to display : "
213 | //<< duration_cast(end - start).count() / 1000.0;
214 | //LOG(INFO) << "waitKey";
215 | const int key = cv::waitKey(30);
216 | if (key == 27 || key == 'q') {
217 | break;
218 | } else if (key == (int)'r') {
219 | // TODO: This is not working 100% correctly
220 | LOG(INFO) << "Reset";
221 | fg_layer.setTo(0);
222 | bg_layer.setTo(0);
223 | matter.reset(new InteractiveMatter(lab_l.get(), lab_a.get(),
224 | lab_b.get(), W, H));
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/unix/samples/main.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include
14 | #include
15 |
16 | #include "utils.h"
17 | #include "kde.h"
18 | #include "geodesic.h"
19 | #include "matting.h"
20 |
21 | #include "cvutils.h"
22 |
23 | using boost::scoped_ptr;
24 | using boost::scoped_array;
25 | using namespace std;
26 |
27 | void SaveVector(const string& filename, const vector& v) {
28 | fstream f(filename.c_str(), fstream::out);
29 | for (size_t i = 0; i < v.size(); ++i) {
30 | f << v[i] << "\t";
31 | }
32 | f.close();
33 | }
34 |
35 | // Saves foreground and background density estimation to fname
36 | // channels should be an array of 3 uint8_t W*H array containing channel data
37 | // Those can then be displayed using the plot_densities.py script
38 | void SaveForegroundBackgroundDensities(const uint8_t** channels,
39 | const uint8_t* fg,
40 | const uint8_t* bg,
41 | int W,
42 | int H,
43 | bool median_filter,
44 | const string& fname) {
45 | // For each channel, foreground and background probabilities
46 | vector> fg_prob(3), bg_prob(3);
47 | for (int i = 0; i < 3; ++i) {
48 | ColorChannelKDE(channels[i], fg, W, H, median_filter, &fg_prob[i]);
49 | ColorChannelKDE(channels[i], bg, W, H, median_filter, &bg_prob[i]);
50 | }
51 |
52 | fstream f(fname.c_str(), fstream::out);
53 | for (int c = 0; c < 3; ++c) {
54 | for (int k = 0; k < 2; ++k) { // k == 0 => fg, k == 1 => bg
55 | const vector& v = (k == 0) ? fg_prob[c] : bg_prob[c];
56 | for (size_t i = 0; i < v.size(); ++i) {
57 | f << v[i] << "\t";
58 | }
59 | f << endl;
60 | }
61 | }
62 | f.close();
63 | }
64 |
65 | int main(int argc, char** argv) {
66 | //const string imgname = "data/alphamatting.com/GT18";
67 | const string imgname = "data/front";
68 | //const string imgname = "data/cat";
69 |
70 | // Load input image
71 | cv::Mat img = cv::imread(imgname + ".png", CV_LOAD_IMAGE_COLOR);
72 | CHECK(img.data);
73 |
74 | // Load scribbles and binarize them such that pixels drawn by the user
75 | // have a value of 255
76 | cv::Mat scribble_fg = cv::imread(imgname + "_FG.png",
77 | CV_LOAD_IMAGE_GRAYSCALE);
78 | CHECK(scribble_fg.data);
79 | cv::threshold(scribble_fg, scribble_fg, 1, 255, cv::THRESH_BINARY_INV);
80 |
81 | cv::Mat scribble_bg = cv::imread(imgname + "_BG.png",
82 | CV_LOAD_IMAGE_GRAYSCALE);
83 | CHECK(scribble_bg.data);
84 | cv::threshold(scribble_bg, scribble_bg, 1, 255, cv::THRESH_BINARY_INV);
85 |
86 | cv::Mat img_lab;
87 | // TODO: HSV seems to work much better
88 | //cv::cvtColor(img, img_lab, CV_BGR2HSV);
89 | cv::cvtColor(img, img_lab, CV_BGR2Lab);
90 | CHECK_EQ(img_lab.type(), CV_8UC3);
91 |
92 | // Split the image channels, getting a direct pointer to memory
93 | const int W = img.cols;
94 | const int H = img.rows;
95 | CHECK_EQ(scribble_fg.cols, W);
96 | CHECK_EQ(scribble_fg.rows, H);
97 | CHECK_EQ(scribble_bg.cols, W);
98 | CHECK_EQ(scribble_bg.rows, H);
99 |
100 | LOG(INFO) << "W = " << W << ", H = " << H;
101 |
102 | // Note: OpenCV's Mat uses row major storage
103 | vector lab;
104 | scoped_array lab_l(new uint8_t[W*H]);
105 | lab.push_back(cv::Mat(H, W, CV_8U, lab_l.get()));
106 | scoped_array lab_a(new uint8_t[W*H]);
107 | lab.push_back(cv::Mat(H, W, CV_8U, lab_a.get()));
108 | scoped_array lab_b(new uint8_t[W*H]);
109 | lab.push_back(cv::Mat(H, W, CV_8U, lab_b.get()));
110 | cv::split(img_lab, lab);
111 |
112 | scoped_array fg(new uint8_t[W*H]);
113 | {
114 | cv::Mat fg_mat(H, W, CV_8U, fg.get());
115 | scribble_fg.copyTo(fg_mat);
116 | }
117 | scoped_array bg(new uint8_t[W*H]);
118 | {
119 | cv::Mat bg_mat(H, W, CV_8U, bg.get());
120 | scribble_bg.copyTo(bg_mat);
121 | }
122 |
123 | const uint8_t* channels[3] = {lab_l.get(), lab_a.get(), lab_b.get()};
124 | // Export densities for plot_densities.py script
125 | if (true) {
126 | SaveForegroundBackgroundDensities(channels, fg.get(), bg.get(), W, H,
127 | false, "densities_nomedfilter.txt");
128 | SaveForegroundBackgroundDensities(channels, fg.get(), bg.get(), W, H,
129 | true, "densities_medfilter.txt");
130 | LOG(INFO) << "Saved per-channel PDF to .txt";
131 | }
132 |
133 | ShowImage(scribble_fg, "scribble fg", false);
134 | ShowImage(scribble_bg, "scribble bg", false);
135 |
136 | // Color PDF
137 | scoped_array fg_pdf(new double[W*H]);
138 | scoped_array bg_pdf(new double[W*H]);
139 | ImageColorPDF(channels, fg.get(), W, H, fg_pdf.get());
140 | // Caution : at first, it might seem bg_pdf = 1 - fg_pdf, but this is not
141 | // the case
142 | ImageColorPDF(channels, bg.get(), W, H, bg_pdf.get());
143 |
144 | cv::Mat fg_pdf_mat(H, W, CV_64F, fg_pdf.get());
145 | cv::Mat bg_pdf_mat(H, W, CV_64F, bg_pdf.get());
146 | ImageSC(fg_pdf_mat, "fg_pdf", false);
147 | ImageSC(bg_pdf_mat, "bg_pdf", false);
148 | //WaitForEsc();
149 | //return EXIT_SUCCESS;
150 |
151 | // Foreground/Background likelihood
152 | scoped_array fg_like(new double[W*H]);
153 | scoped_array bg_like(new double[W*H]);
154 | cv::Mat fg_like_mat(H, W, CV_64F, fg_like.get());
155 | cv::Mat bg_like_mat(H, W, CV_64F, bg_like.get());
156 | ForegroundLikelihood(fg_pdf.get(), bg_pdf.get(), H, W, fg_like.get());
157 | ForegroundLikelihood(bg_pdf.get(), fg_pdf.get(), H, W, bg_like.get());
158 |
159 | ImageSC(fg_like_mat, "fg_likelihood", false);
160 | ImageSC(bg_like_mat, "bg_likelihood", false);
161 |
162 | // Distance maps
163 | scoped_array fg_dist(new double[W*H]);
164 | scoped_array bg_dist(new double[W*H]);
165 | cv::Mat fg_dist_mat(H, W, CV_64F, fg_dist.get());
166 | cv::Mat bg_dist_mat(H, W, CV_64F, bg_dist.get());
167 | CHECK(scribble_fg.isContinuous());
168 | CHECK(scribble_bg.isContinuous());
169 | GeodesicDistanceMap(scribble_fg.ptr(0), fg_like.get(), W, H,
170 | fg_dist.get());
171 | GeodesicDistanceMap(scribble_bg.ptr(0), bg_like.get(), W, H,
172 | bg_dist.get());
173 |
174 | // Show both distance maps with the same range
175 | double min1, min2, max1, max2;
176 | minMaxIdx(fg_dist_mat, &min1, &max1, NULL, NULL);
177 | minMaxIdx(bg_dist_mat, &min2, &max2, NULL, NULL);
178 | const double Amin = min(min1, min2);
179 | const double Amax = max(max1, max2);
180 |
181 | ImageSC(fg_dist_mat, "fg_dist", false, true, Amin, Amax);
182 | ImageSC(bg_dist_mat, "bg_dist", false, true, Amin, Amax);
183 |
184 | // Segmentation
185 | cv::Mat fgmask = fg_dist_mat < bg_dist_mat;
186 | CHECK_EQ(fgmask.type(), CV_8U);
187 | ImageSC(fgmask, "fgmask", false);
188 |
189 | //ShowImage(lab[0], "lab[0]", true);
190 |
191 | cv::Mat result;
192 | img.copyTo(result, fgmask);
193 | result.setTo(cv::Scalar(255,255,255), ~fgmask);
194 | ShowImage(result, "result", false);
195 | ShowImage(img, "image", true);
196 | return 0;
197 | }
198 |
--------------------------------------------------------------------------------
/unix/samples/simple_interactive.cc:
--------------------------------------------------------------------------------
1 | // SimpleMatter demo
2 | #include
3 |
4 | #include
5 |
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | #include "cvutils.h"
12 |
13 | #include "api.h"
14 |
15 | using boost::scoped_ptr;
16 | using boost::scoped_array;
17 | using namespace std;
18 | using namespace cv;
19 | using namespace std::chrono;
20 |
21 | Mat fg_layer, bg_layer;
22 |
23 | enum DrawMode {
24 | DRAW_BG,
25 | DRAW_FG
26 | };
27 |
28 | DrawMode draw_mode = DRAW_BG;
29 | bool drawing = false;
30 | scoped_ptr matter;
31 |
32 | bool changed = true;
33 |
34 | // Used to track cursor movements between two MOUSEMOVE events. For example
35 | // on OSX, it seems the MOUSEMOVE events have a somewhat important interval
36 | // between them
37 | int x_prev = -1, y_prev = -1;
38 |
39 | const int SCRIBLE_RADIUS = 5;
40 |
41 | static void DrawScribbleCircle(int x, int y) {
42 | const int radius = SCRIBLE_RADIUS;
43 | const int rr = radius*radius;
44 | for (int dx = -radius; dx <=radius; ++dx) {
45 | for (int dy = -radius; dy <=radius; ++dy) {
46 | if ((dx*dx + dy*dy) <= rr) {
47 | if (draw_mode == DRAW_FG) {
48 | fg_layer.at(y + dy, x + dx) = 255;
49 | } else {
50 | bg_layer.at(y + dy, x + dx) = 255;
51 | }
52 | }
53 | }
54 | }
55 | }
56 |
57 | static float Length(const int* vec) {
58 | return sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
59 | }
60 |
61 | static void UpdateMatter() {
62 | LOG(INFO) << "-- Updating masks";
63 | // Opencv uses row-major like libseg
64 | CHECK(fg_layer.isContinuous());
65 | CHECK(bg_layer.isContinuous());
66 | matter->UpdateMasks(bg_layer.ptr(), fg_layer.ptr());
67 | changed = true;
68 | LOG(INFO) << "-- done";
69 | }
70 |
71 | static void OnMouse(int event, int x, int y, int, void*) {
72 | if (event == EVENT_LBUTTONDOWN) {
73 | drawing = true;
74 | draw_mode = DRAW_FG;
75 | x_prev = x;
76 | y_prev = y;
77 | } else if (event == EVENT_RBUTTONDOWN) {
78 | drawing = true;
79 | draw_mode = DRAW_BG;
80 | x_prev = x;
81 | y_prev = y;
82 | } else if (event == EVENT_MOUSEMOVE && drawing) {
83 | if (x_prev != -1 && y_prev != -1) {
84 | // Draw scribbles on a line between (x_prev, y_prev) and (x,y), spaced
85 | // by SCRIBBLE_RADIUS
86 | const int d[2] = { x - x_prev, y - y_prev };
87 | const float len = Length(d);
88 | const float nsteps = len / (float)SCRIBLE_RADIUS;
89 | if (nsteps > 0) {
90 | const float tstep = 1.0f / nsteps;
91 | for (float t = 0; t < 1.0f; t += tstep) {
92 | const int curr_x = (int)(x_prev + t * d[0]);
93 | const int curr_y = (int)(y_prev + t * d[1]);
94 | DrawScribbleCircle(curr_x, curr_y);
95 | }
96 | }
97 | x_prev = x;
98 | y_prev = y;
99 | }
100 | } else if (event == EVENT_LBUTTONUP || event == EVENT_RBUTTONUP) {
101 | UpdateMatter();
102 | drawing = false;
103 | x_prev = -1;
104 | y_prev = -1;
105 | }
106 | }
107 |
108 | int main(int argc, char** argv) {
109 | string imgname = "data/default_img.jpg";
110 | if (argc > 1) {
111 | imgname = argv[1];
112 | }
113 |
114 | Mat img = imread(imgname, CV_LOAD_IMAGE_COLOR);
115 | CHECK(img.data);
116 |
117 | fg_layer = Mat::zeros(img.rows, img.cols, CV_8UC1);
118 | bg_layer = Mat::zeros(img.rows, img.cols, CV_8UC1);
119 |
120 | namedWindow("img", 0);
121 | setMouseCallback("img", OnMouse, 0);
122 | imshow("img", img);
123 |
124 | //namedWindow("matted", 0);
125 |
126 | cout << "Instructions : " << endl;
127 | cout << "- left click to draw foreground scribbles" << endl;
128 | cout << "- right click to draw background scribbles" << endl;
129 |
130 | const int W = img.cols;
131 | const int H = img.rows;
132 |
133 | // RGB -> lab
134 | cv::Mat img_lab;
135 | cv::cvtColor(img, img_lab, CV_BGR2Lab);
136 | vector lab;
137 | scoped_array lab_l(new uint8_t[W*H]);
138 | lab.push_back(cv::Mat(H, W, CV_8U, lab_l.get()));
139 | scoped_array lab_a(new uint8_t[W*H]);
140 | lab.push_back(cv::Mat(H, W, CV_8U, lab_a.get()));
141 | scoped_array lab_b(new uint8_t[W*H]);
142 | lab.push_back(cv::Mat(H, W, CV_8U, lab_b.get()));
143 | cv::split(img_lab, lab);
144 |
145 | matter.reset(new SimpleMatter(lab_l.get(), lab_a.get(), lab_b.get(),
146 | W, H));
147 |
148 | scoped_array fg_likelihood(new double[W*H]);
149 | cv::Mat fg_likelihood_mat(H, W, CV_64F, fg_likelihood.get());
150 | fg_likelihood_mat.setTo(0);
151 | scoped_array bg_likelihood(new double[W*H]);
152 | cv::Mat bg_likelihood_mat(H, W, CV_64F, bg_likelihood.get());
153 | bg_likelihood_mat.setTo(0);
154 |
155 | scoped_array fg_dist(new double[W*H]);
156 | Mat fg_dist_mat(H, W, CV_64F, fg_dist.get());
157 | fg_dist_mat.setTo(0);
158 | scoped_array bg_dist(new double[W*H]);
159 | Mat bg_dist_mat(H, W, CV_64F, bg_dist.get());
160 | bg_dist_mat.setTo(0);
161 |
162 | scoped_array final_mask(new uint8_t[W*H]);
163 | cv::Mat final_mask_mat(H, W, CV_8UC1, final_mask.get());
164 | final_mask_mat.setTo(0);
165 |
166 | Mat result(img.rows, img.cols, img.type(), Scalar::all(0));
167 |
168 | while (true) {
169 | auto start = high_resolution_clock::now();
170 | // http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_core/py_image_arithmetics/py_image_arithmetics.html
171 | // Display scribbles on top of images :
172 | // - fg scribbles are green
173 | // - bg scribbles are red
174 | // - fg and bg scribbles intersections (bad) are pink
175 | Mat disp_img;
176 | img.copyTo(disp_img);
177 | CHECK_EQ(disp_img.type(), CV_8UC3);
178 |
179 | disp_img.setTo(Scalar(0, 255, 0), fg_layer);
180 | disp_img.setTo(Scalar(0, 0, 255), bg_layer);
181 | {
182 | Mat layer_overlap;
183 | bitwise_and(fg_layer, bg_layer, layer_overlap);
184 | disp_img.setTo(Scalar(255, 105, 180), layer_overlap);
185 | }
186 | imshow("img", disp_img);
187 |
188 | // Refresh images from matter if something has changed
189 | if (changed) {
190 | LOG(INFO) << "Refreshing from matter";
191 | matter->GetForegroundLikelihood(fg_likelihood.get());
192 | matter->GetBackgroundLikelihood(bg_likelihood.get());
193 | matter->GetForegroundDist(fg_dist.get());
194 | matter->GetBackgroundDist(bg_dist.get());
195 | matter->GetForegroundMask(final_mask.get());
196 | changed = false;
197 | }
198 | ImageSC(fg_likelihood_mat, "fg_likelihood", false, false);
199 | ImageSC(bg_likelihood_mat, "bg_likelihood", false, false);
200 |
201 | ImageSC(fg_dist_mat, "fg_dist", false, false);
202 | ImageSC(bg_dist_mat, "bg_dist", false, false);
203 |
204 | ImageSC(final_mask_mat, "final_mask", false, false);
205 |
206 | result.setTo(0);
207 | img.copyTo(result, final_mask_mat);
208 | ShowImage(result, "result", false);
209 |
210 | auto end = high_resolution_clock::now();
211 | //LOG(INFO) << "time to display : "
212 | //<< duration_cast(end - start).count() / 1000.0;
213 | //LOG(INFO) << "waitKey";
214 | const int key = cv::waitKey(30);
215 | if (key == 27 || key == 'q') {
216 | break;
217 | } else if (key == (int)'r') {
218 | LOG(INFO) << "Reset";
219 | fg_layer.setTo(0);
220 | bg_layer.setTo(0);
221 | matter.reset(new SimpleMatter(lab_l.get(), lab_a.get(),
222 | lab_b.get(), W, H));
223 | UpdateMatter();
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/unix/third_party/gmock-1.7.0/CHANGES:
--------------------------------------------------------------------------------
1 | Changes for 1.7.0:
2 |
3 | * All new improvements in Google Test 1.7.0.
4 | * New feature: matchers DoubleNear(), FloatNear(),
5 | NanSensitiveDoubleNear(), NanSensitiveFloatNear(),
6 | UnorderedElementsAre(), UnorderedElementsAreArray(), WhenSorted(),
7 | WhenSortedBy(), IsEmpty(), and SizeIs().
8 | * Improvement: Google Mock can now be built as a DLL.
9 | * Improvement: when compiled by a C++11 compiler, matchers AllOf()
10 | and AnyOf() can accept an arbitrary number of matchers.
11 | * Improvement: when compiled by a C++11 compiler, matchers
12 | ElementsAreArray() can accept an initializer list.
13 | * Improvement: when exceptions are enabled, a mock method with no
14 | default action now throws instead crashing the test.
15 | * Improvement: added class testing::StringMatchResultListener to aid
16 | definition of composite matchers.
17 | * Improvement: function return types used in MOCK_METHOD*() macros can
18 | now contain unprotected commas.
19 | * Improvement (potentially breaking): EXPECT_THAT() and ASSERT_THAT()
20 | are now more strict in ensuring that the value type and the matcher
21 | type are compatible, catching potential bugs in tests.
22 | * Improvement: Pointee() now works on an optional.
23 | * Improvement: the ElementsAreArray() matcher can now take a vector or
24 | iterator range as input, and makes a copy of its input elements
25 | before the conversion to a Matcher.
26 | * Improvement: the Google Mock Generator can now generate mocks for
27 | some class templates.
28 | * Bug fix: mock object destruction triggerred by another mock object's
29 | destruction no longer hangs.
30 | * Improvement: Google Mock Doctor works better with newer Clang and
31 | GCC now.
32 | * Compatibility fixes.
33 | * Bug/warning fixes.
34 |
35 | Changes for 1.6.0:
36 |
37 | * Compilation is much faster and uses much less memory, especially
38 | when the constructor and destructor of a mock class are moved out of
39 | the class body.
40 | * New matchers: Pointwise(), Each().
41 | * New actions: ReturnPointee() and ReturnRefOfCopy().
42 | * CMake support.
43 | * Project files for Visual Studio 2010.
44 | * AllOf() and AnyOf() can handle up-to 10 arguments now.
45 | * Google Mock doctor understands Clang error messages now.
46 | * SetArgPointee<> now accepts string literals.
47 | * gmock_gen.py handles storage specifier macros and template return
48 | types now.
49 | * Compatibility fixes.
50 | * Bug fixes and implementation clean-ups.
51 | * Potentially incompatible changes: disables the harmful 'make install'
52 | command in autotools.
53 |
54 | Potentially breaking changes:
55 |
56 | * The description string for MATCHER*() changes from Python-style
57 | interpolation to an ordinary C++ string expression.
58 | * SetArgumentPointee is deprecated in favor of SetArgPointee.
59 | * Some non-essential project files for Visual Studio 2005 are removed.
60 |
61 | Changes for 1.5.0:
62 |
63 | * New feature: Google Mock can be safely used in multi-threaded tests
64 | on platforms having pthreads.
65 | * New feature: function for printing a value of arbitrary type.
66 | * New feature: function ExplainMatchResult() for easy definition of
67 | composite matchers.
68 | * The new matcher API lets user-defined matchers generate custom
69 | explanations more directly and efficiently.
70 | * Better failure messages all around.
71 | * NotNull() and IsNull() now work with smart pointers.
72 | * Field() and Property() now work when the matcher argument is a pointer
73 | passed by reference.
74 | * Regular expression matchers on all platforms.
75 | * Added GCC 4.0 support for Google Mock Doctor.
76 | * Added gmock_all_test.cc for compiling most Google Mock tests
77 | in a single file.
78 | * Significantly cleaned up compiler warnings.
79 | * Bug fixes, better test coverage, and implementation clean-ups.
80 |
81 | Potentially breaking changes:
82 |
83 | * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher()
84 | need to be updated after upgrading to Google Mock 1.5.0; matchers defined
85 | using MATCHER or MATCHER_P* aren't affected.
86 | * Dropped support for 'make install'.
87 |
88 | Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of
89 | Google Test):
90 |
91 | * Works in more environments: Symbian and minGW, Visual C++ 7.1.
92 | * Lighter weight: comes with our own implementation of TR1 tuple (no
93 | more dependency on Boost!).
94 | * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks.
95 | * New feature: ACTION_TEMPLATE for defining templatized actions.
96 | * New feature: the .After() clause for specifying expectation order.
97 | * New feature: the .With() clause for for specifying inter-argument
98 | constraints.
99 | * New feature: actions ReturnArg(), ReturnNew(...), and
100 | DeleteArg().
101 | * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(),
102 | and Contains().
103 | * New feature: utility class MockFunction, useful for checkpoints, etc.
104 | * New feature: functions Value(x, m) and SafeMatcherCast(m).
105 | * New feature: copying a mock object is rejected at compile time.
106 | * New feature: a script for fusing all Google Mock and Google Test
107 | source files for easy deployment.
108 | * Improved the Google Mock doctor to diagnose more diseases.
109 | * Improved the Google Mock generator script.
110 | * Compatibility fixes for Mac OS X and gcc.
111 | * Bug fixes and implementation clean-ups.
112 |
113 | Changes for 1.1.0:
114 |
115 | * New feature: ability to use Google Mock with any testing framework.
116 | * New feature: macros for easily defining new matchers
117 | * New feature: macros for easily defining new actions.
118 | * New feature: more container matchers.
119 | * New feature: actions for accessing function arguments and throwing
120 | exceptions.
121 | * Improved the Google Mock doctor script for diagnosing compiler errors.
122 | * Bug fixes and implementation clean-ups.
123 |
124 | Changes for 1.0.0:
125 |
126 | * Initial Open Source release of Google Mock
127 |
--------------------------------------------------------------------------------
/unix/third_party/gmock-1.7.0/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | # This file contains a list of people who've made non-trivial
2 | # contribution to the Google C++ Mocking Framework project. People
3 | # who commit code to the project are encouraged to add their names
4 | # here. Please keep the list sorted by first names.
5 |
6 | Benoit Sigoure
7 | Bogdan Piloca
8 | Chandler Carruth
9 | Dave MacLachlan
10 | David Anderson
11 | Dean Sturtevant
12 | Gene Volovich
13 | Hal Burch
14 | Jeffrey Yasskin
15 | Jim Keller
16 | Joe Walnes
17 | Jon Wray
18 | Keir Mierle
19 | Keith Ray
20 | Kostya Serebryany
21 | Lev Makhlis
22 | Manuel Klimek
23 | Mario Tanev
24 | Mark Paskin
25 | Markus Heule
26 | Matthew Simmons
27 | Mike Bland
28 | Neal Norwitz
29 | Nermin Ozkiranartli
30 | Owen Carlsen
31 | Paneendra Ba
32 | Paul Menage
33 | Piotr Kaminski
34 | Russ Rufer
35 | Sverre Sundsdal
36 | Takeshi Yoshino
37 | Vadim Berman
38 | Vlad Losev
39 | Wolfgang Klier
40 | Zhanyong Wan
41 |
--------------------------------------------------------------------------------
/unix/third_party/gmock-1.7.0/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2008, Google Inc.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are
6 | met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Google Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/unix/third_party/gmock-1.7.0/gtest/CHANGES:
--------------------------------------------------------------------------------
1 | Changes for 1.7.0:
2 |
3 | * New feature: death tests are supported on OpenBSD and in iOS
4 | simulator now.
5 | * New feature: Google Test now implements a protocol to allow
6 | a test runner to detect that a test program has exited
7 | prematurely and report it as a failure (before it would be
8 | falsely reported as a success if the exit code is 0).
9 | * New feature: Test::RecordProperty() can now be used outside of the
10 | lifespan of a test method, in which case it will be attributed to
11 | the current test case or the test program in the XML report.
12 | * New feature (potentially breaking): --gtest_list_tests now prints
13 | the type parameters and value parameters for each test.
14 | * Improvement: char pointers and char arrays are now escaped properly
15 | in failure messages.
16 | * Improvement: failure summary in XML reports now includes file and
17 | line information.
18 | * Improvement: the