[
  {
    "path": ".gitignore",
    "content": ".idea/\nbuild/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8)\n\nproject(tracker)\n  \nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wall -O3\")\nif(CMAKE_COMPILER_IS_GNUCXX)\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -std=c++11 -Wextra -pedantic -Wno-long-long\")\nendif()\n\nFIND_PACKAGE( OpenCV REQUIRED )\nlink_directories ( ${OpenCV_LIB_DIR} )\nMESSAGE(STATUS \"OpenCV_LIB_DIR: ${OpenCV_LIB_DIR}\")\n\ninclude_directories ( ${OpenCV_INCLUDE_DIRS} )\nMESSAGE(STATUS \"OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}\")\n\ninclude_directories(${CMAKE_SOURCE_DIR}/src/)\n\nadd_subdirectory(src)\n\nadd_executable(asms_vot main_vot.cpp)\ntarget_link_libraries(asms_vot ${OpenCV_LIBS} color)\n\nadd_executable(asms_demo main_demo.cpp )\ntarget_link_libraries(asms_demo ${OpenCV_LIBS} color )\n\n# Try to find TraX header and library ...\nFIND_PACKAGE(trax REQUIRED COMPONENTS core opencv)\nIF (trax_FOUND)\n    MESSAGE(STATUS \"Trax protocol found: building asms_trax\")\n    INCLUDE_DIRECTORIES(${TRAX_INCLUDE_DIRS})\n    LINK_DIRECTORIES(${TRAX_LIBRARY_DIRS})\n\n    add_executable(asms_trax main_trax.cpp)\n    target_link_libraries(asms_trax ${OpenCV_LIBS} color ${TRAX_LIBRARIES})\nENDIF()\n\n\n\n\n"
  },
  {
    "path": "README.md",
    "content": "## Robust scale-adaptive mean-shift for tracking\n\nAuthors : Tomas Vojir, Jana Noskova, Jiri Matas\n\n________________\n\nThis C++ code implements a tracking pipeline of Scale Adaptive Mean-Shift method.\n\nIt is free for research use. If you find it useful or use it in your research, please cite the [1] paper.\n\nThe code depends on OpenCV 2.4+ library and is build via cmake toolchain.\n\n_________________\nQuick start guide\n\nfor linux: open terminal in the directory with the code\n\n$ mkdir build; cd build; cmake .. ; make\n\nThis code compiles into two binaries **demo** and **asms_vot**\n\n./asms_demo  \n- run live demo with visual output\n- control : object is selected by mouse, click and drag mouse to select rectandle\n  - ESC = quit\n  - i = init new object\n\n./asms_vot \n- using VOT 2015 methodology (http://www.votchallenge.net/), is backward compatible with older ones\n - INPUT : expecting two files, images.txt (list of sequence images with absolute path) and \n                   region.txt with initial bounding box in the first frame in format \"top_left_x, top_left_y, width, height\"\n - OUTPUT : output.txt containing the bounding boxes in the same format as region.txt\n\n./asms_trax\n- using VOT2014+ trax protocol\n- require [trax](https://github.com/votchallenge/trax) library to be compiled\n  with opencv support and installed. See trax instruction for compiling and\n  installing.\n\n__________\nReferences\n\n[1] Tomas Vojir, Jana Noskova and Jiri Matas, “Robust scale-adaptive mean-shift for tracking“. \n    Pattern Recognition Letters 2014.\n\n_____________________________________\nCopyright (c) 2014, Tomáš Vojíř\n\nPermission to use, copy, modify, and distribute this software for research\npurposes is hereby granted, provided that the above copyright notice and \nthis permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "main_demo.cpp",
    "content": "#include <iostream>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n#include \"highgui.h\"\n#include \"colotracker.h\"\n#include \"region.h\"\n#include <string>\n\nusing namespace cv;\n\ncv::Point g_topLeft(0,0);\ncv::Point g_botRight(0,0);\ncv::Point g_botRight_tmp(0,0);\nbool plot = false;\nbool g_trackerInitialized = false;\nColorTracker * g_tracker = NULL;\n\nstatic void onMouse( int event, int x, int y, int, void* param)\n{\n    cv::Mat img = ((cv::Mat *)param)->clone();\n    if( event == cv::EVENT_LBUTTONDOWN && !g_trackerInitialized){\n        std::cout << \"DOWN \" << std::endl;\n        g_topLeft = Point(x,y);\n        plot = true;\n    }else if (event == cv::EVENT_LBUTTONUP && !g_trackerInitialized){\n        std::cout << \"UP \" << std::endl;\n        g_botRight = Point(x,y);\n        plot = false;\n        if (g_tracker != NULL)\n            delete g_tracker;\n        g_tracker = new ColorTracker();\n        g_tracker->init(*(cv::Mat *)param, g_topLeft.x, g_topLeft.y, g_botRight.x, g_botRight.y);\n        g_trackerInitialized = true;\n    }else if (event == cv::EVENT_MOUSEMOVE && !g_trackerInitialized){\n        //plot bbox\n        g_botRight_tmp = Point(x,y);\n        // if (plot){\n        //     cv::rectangle(img, g_topLeft, current, cv::Scalar(0,255,0), 2);\n        //     imshow(\"output\", img);\n        // }\n    }\n}\n\n\nint main(int argc, char **argv) \n{\n    BBox * bb = NULL;\n    cv::Mat img;\n    int captureDevice = 0;\n    if (argc > 1)\n        captureDevice = atoi(argv[1]);\n\n    cv::VideoCapture webcam = cv::VideoCapture(captureDevice);\n    webcam.set(CV_CAP_PROP_FRAME_WIDTH, 640);\n    webcam.set(CV_CAP_PROP_FRAME_HEIGHT, 480);\n    if (!webcam.isOpened()){\n        webcam.release();\n        std::cerr << \"Error during opening capture device!\" << std::endl;\n        return 1;\n    }\n\n    cv::namedWindow( \"output\", 0 );\n    cv::setMouseCallback( \"output\", onMouse, &img);\n\n    for(;;){\n\n        webcam >> img; \n\n        int c = waitKey(10);\n        if( (c & 255) == 27 ) {\n            std::cout << \"Exiting ...\" << std::endl;\n            break;\n        }\n        //some control\n        switch( (char)c ){\n        case 'i':\n            g_trackerInitialized = false;\n            g_topLeft = cv::Point(0,0);\n            g_botRight_tmp = cv::Point(0,0);\n            break;\n        default:;\n\n        }\n\n        if (g_trackerInitialized && g_tracker != NULL){\n            bb = g_tracker->track(img);\n        }\n\n        if (!g_trackerInitialized && plot && g_botRight_tmp.x > 0){\n            cv::rectangle(img, g_topLeft, g_botRight_tmp, cv::Scalar(0,255,0), 2);\n        }\n\n        if (bb != NULL){\n            cv::rectangle(img, Point2i(bb->x, bb->y), Point2i(bb->x + bb->width, bb->y + bb->height), Scalar(255, 0, 0), 3);\n            //cv::rectangle(img, Point2i(bb->x-5, bb->y-5), Point2i(bb->x + bb->width+5, bb->y + bb->height+5), Scalar(0, 0, 255), 1);\n            delete bb;\n            bb = NULL;\n        }\n\n        cv::imshow(\"output\", img);\n\n    }\n\n    if (g_tracker != NULL)\n        delete g_tracker;\n    return 0;\n}\n"
  },
  {
    "path": "main_trax.cpp",
    "content": "#include <iostream>\n#include <opencv2/imgproc/imgproc.hpp>\n#include \"colotracker.h\"\n#include \"region.h\"\n#include <string>\n#include <limits>\n#include <trax/opencv.hpp>\n#include <memory>\n\nusing namespace cv;\n\nint main(int, char **)\n{\n    trax::ImageList img;\n    trax::Region reg;\n\n    std::unique_ptr<ColorTracker> tracker;\n    cv::Mat image;\n\tcv::Rect rectangle;\n\n    trax::Server handle(trax::Metadata(TRAX_REGION_RECTANGLE, TRAX_IMAGE_PATH | TRAX_IMAGE_MEMORY | TRAX_IMAGE_BUFFER, TRAX_CHANNEL_COLOR), trax_no_log);\n\n    while(true) {\n\n        trax::Properties prop;\n        trax::Region status;\n\n        int tr = handle.wait(img, reg, prop);\n\n        if (tr == TRAX_INITIALIZE) {\n            rectangle = trax::region_to_rect(reg);\n            image = trax::image_to_mat(img.get(TRAX_CHANNEL_COLOR));\n\n            tracker.reset(new ColorTracker());\n\n\t\t\ttracker->init(image, rectangle.x, rectangle.y, rectangle.x + rectangle.width, rectangle.y + rectangle.height);\n\n            status = trax::rect_to_region(rectangle);\n\n        } else if (tr == TRAX_FRAME) {\n\n            image = trax::image_to_mat(img.get(TRAX_CHANNEL_COLOR));\n\t\t\tBBox* bb = tracker->track(image);\n\n            if (bb != NULL) {\n\t\t\t    rectangle = cv::Rect(bb->x, bb->y, bb->width, bb->height);\n                status = trax::rect_to_region(rectangle);\n                delete bb;\n            } else {\n                status = trax::Region::create_special(0);\n            }\n\n        } else {\n            break;\n        }\n\n        handle.reply(status, trax::Properties());\n\n    }\n\n    return EXIT_SUCCESS;\n\n}\n"
  },
  {
    "path": "main_vot.cpp",
    "content": "#include <iostream>\n#include <opencv2/imgproc/imgproc.hpp>\n#include \"colotracker.h\"\n#include \"region.h\"\n#include <string>\n#include <limits>\n\n#include \"vot.hpp\"\n\nusing namespace cv;\n\nint main(int, char **) \n{\n    ColorTracker tracker;\n    cv::Mat image;\n\n    //load region, images and prepare for output\n    VOT vot_io(\"region.txt\", \"images.txt\", \"output.txt\");\n    \n    //img = firts frame, initPos = initial position in the first frame\n    cv::Rect init_rect = vot_io.getInitRectangle();\n    vot_io.outputBoundingBox(init_rect);\n    vot_io.getNextImage(image);\n\n    //tracker initialization\n    tracker.init(image, init_rect.x, init_rect.y, init_rect.x + init_rect.width, init_rect.y + init_rect.height);\n\n    //track   \n    double average_speed_ms = 0.0;\n    double num_frames = 0.0;\n    while (vot_io.getNextImage(image) == 1){\n        double time_profile_counter = cv::getCPUTickCount();\n        BBox * bb = tracker.track(image);\n        time_profile_counter = cv::getCPUTickCount() - time_profile_counter;\n        average_speed_ms += time_profile_counter/((double)cvGetTickFrequency()*1000);\n        num_frames += 1.0;\n\n        if (bb != NULL){\n            vot_io.outputBoundingBox(cv::Rect(bb->x, bb->y, bb->width, bb->height));\n            delete bb;\n        } else {\n            vot_io.outputBoundingBox(cv::Rect(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()));\n        }\n    }\n\n    std::cout << \"Average speed \" <<  average_speed_ms/num_frames << \"ms. (\" << 1000.0/(average_speed_ms/num_frames) << \"fps)\" << std::endl;\n\n    return 0;\n}\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8)\n\nset(COLOR_LIB_SRC colotracker.cpp histogram.cpp region.cpp)\n\nadd_library(color STATIC ${COLOR_LIB_SRC})\ntarget_link_libraries(color ${OpenCV_LIBS})\nset_target_properties(color PROPERTIES VERSION 1.0.0 SOVERSION 1)"
  },
  {
    "path": "src/colotracker.cpp",
    "content": "#include \"colotracker.h\"\n#include <iostream>\n#include <fstream>\n\nvoid ColorTracker::init(cv::Mat & img, int x1, int y1, int x2, int y2)\n{\n    im1 = cv::Mat( img.rows, img.cols, CV_8UC1 );\n    im2 = cv::Mat( img.rows, img.cols, CV_8UC1 );\n    im3 = cv::Mat( img.rows, img.cols, CV_8UC1 );\n\n    //boundary checks\n    y1 = std::max(0, y1);\n    y2 = std::min(img.rows-1, y2);\n    x1 = std::max(0, x1);\n    x2 = std::min(img.cols-1, x2);\n\n    preprocessImage(img);\n\n    extractForegroundHistogram(x1, y1, x2, y2, q_hist);\n    q_orig_hist = q_hist;\n\n    extractBackgroundHistogram(x1, y1, x2, y2, b_hist);\n\n    Histogram b_weights = b_hist;\n\n    b_weights.transformToWeights();\n    q_hist.multiplyByWeights(&b_weights);\n\n    lastPosition.setBBox(x1, y1, x2-x1, y2-y1, 1, 1);\n    defaultWidth = x2-x1;\n    defaultHeight = y2-y1;\n    sumIter = 0;\n    frame = 0;\n\n\n    double w2 = (x2-x1)/2.;\n    double h2 = (y2-y1)/2.;\n    double cx = x1 + w2;\n    double cy = y1 + h2;\n    double wh = w2+5.;\n    double hh = h2+5.;\n\n    double Sbg = 0, Sfg = 0;\n    for(int i = y1; i < y2+1; i++) {\n        const uchar *Mi1 = im1.ptr<uchar>(i);\n        const uchar *Mi2 = im2.ptr<uchar>(i);\n        const uchar *Mi3 = im3.ptr<uchar>(i);\n        double tmp_y = std::pow((cy - i) / hh, 2);\n        for (int j = x1; j < x2+1; j++) {\n            double arg = std::pow((cx - j) / wh, 2) + tmp_y;\n            if (arg > 1)\n                continue;\n            //likelihood weights\n            double wqi = 1.0;\n            double wbi = sqrt(b_hist.getValue(Mi1[j], Mi2[j], Mi3[j]) /\n                              q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n            Sbg += (wqi < wbi) ? q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j])\n                               : 0.0;\n            Sfg += q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j]);\n        }\n    }\n\n    //wAvgBg = 0.5\n    wAvgBg = std::max(0.1, std::min(Sbg/Sfg, 0.5));\n    bound1 = 0.05;\n    bound2 = 0.1;\n\n}\n\ncv::Point ColorTracker::histMeanShift(double x1, double y1, double x2, double y2)\n{\n    int maxIter = 15;\n\n    double w2 = (x2-x1+1)/2;\n    double h2 = (y2-y1+1)/2;\n\n    double borderX = 5;\n    double borderY = 5;\n\n    double cx = x1 + w2;\n    double cy = y1 + h2;\n\n    Histogram y1hist;\n\n    int ii;\n    for (ii = 0; ii < maxIter; ++ii){\n\n        double wh = (w2+borderX);\n        double hh = (h2+borderY);\n        int rowMin = std::max(0, (int)(cy-hh));\n        int rowMax = std::min(im1.rows, (int)(cy+hh));\n        int colMin = std::max(0, (int)(cx-wh));\n        int colMax = std::min(im1.cols, (int)(cx+wh));\n\n        extractForegroundHistogram(colMin, rowMin, colMax, rowMax, y1hist);\n\n        double batta_q = y1hist.computeSimilarity(&q_orig_hist);\n        double batta_b = y1hist.computeSimilarity(&b_hist);\n\n\n        //MeanShift Vector\n        double m0 = 0, m1x = 0, m1y = 0;\n        for(int i = rowMin; i < rowMax; i++){\n            const uchar* Mi1 = im1.ptr<uchar>(i);\n            const uchar* Mi2 = im2.ptr<uchar>(i);\n            const uchar* Mi3 = im3.ptr<uchar>(i);\n            double tmp_y = std::pow((cy-(float)i)/hh,2);\n            for(int j = colMin; j < colMax; j++){\n                double arg = std::pow((cx-(float)j)/wh,2) + tmp_y;\n                if (arg>1)\n                    continue;\n\n                double wqi = sqrt(q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n                double wbi = sqrt(b_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n\n                double wg = std::max(wqi/batta_q - wbi/batta_b, 0.0)*(-kernelProfile_EpanechnikovDeriv(arg));\n\n                m0 += wg;\n                m1x += (j-cx)*wg;\n                m1y += (i-cy)*wg;\n            }\n        }\n\n        double xn_1 = m1x/m0 + cx;\n        double yn_1 = m1y/m0 + cy;\n\n        if (std::pow(xn_1 - cx,2) + std::pow(yn_1 - cy,2) < 0.1)\n            break;\n\n        if (m0==m0 && !std::isinf(m0) && m0 > 0){\n            cx = xn_1;\n            cy = yn_1;\n        }\n    }\n\n    return  cv::Point(cx, cy);\n}\n\ncv::Point ColorTracker::histMeanShiftIsotropicScale(double x1, double y1, double x2, double y2, double * scale, int * iter)\n{\n    int maxIter = 15;\n\n    double w2 = (x2-x1)/2;\n    double h2 = (y2-y1)/2;\n\n    double borderX = 5;\n    double borderY = 5;\n\n    double cx = x1 + w2;\n    double cy = y1 + h2;\n\n    Histogram y1hist;\n    double h0 = 1;\n\n    int ii;\n    for (ii = 0; ii < maxIter; ++ii){\n\n        double wh = h0*w2+borderX;\n        double hh = h0*h2+borderY;\n        int rowMin = std::max(0, (int)(cy-hh));\n        int rowMax = std::min(im1.rows-1, (int)(cy+hh));\n        int colMin = std::max(0, (int)(cx-wh));\n        int colMax = std::min(im1.cols-1, (int)(cx+wh));\n\n        extractForegroundHistogram(colMin, rowMin, colMax, rowMax, y1hist);\n\n        double batta_q = y1hist.computeSimilarity(&q_orig_hist);\n        double batta_b = y1hist.computeSimilarity(&b_hist);\n\n\n        //MeanShift Vector\n        double m0 = 0, m1x = 0, m1y = 0;\n\n        double wg_dist_sum = 0, wk_sum = 0, Sbg = 0, Sfg = 0;\n\n        for(int i = rowMin; i < rowMax; i++){\n            const uchar* Mi1 = im1.ptr<uchar>(i);\n            const uchar* Mi2 = im2.ptr<uchar>(i);\n            const uchar* Mi3 = im3.ptr<uchar>(i);\n            double tmp_y = std::pow((cy-i)/hh,2);\n            for(int j = colMin; j < colMax; j++){\n                double arg = std::pow((cx-j)/wh,2) + tmp_y;\n                if (arg>1)\n                    continue;\n\n                //likelihood weights\n                double wqi = sqrt(q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n                double wbi = sqrt(b_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n                double w = std::max(wqi/batta_q - wbi/batta_b, 0.0);\n\n                //weights based on \"Robust mean-shift tracking with corrected background-weighted histogram\"\n                // double w = sqrt(q_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n\n                //orig weights\n                // double w = sqrt(q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n\n                double wg = w*(-kernelProfile_EpanechnikovDeriv(arg));\n                double dist = std::sqrt(std::pow((j-cx)/w2,2) + std::pow((i-cy)/h2,2));\n\n                wg_dist_sum += wg*dist;\n                wk_sum += w*(kernelProfile_Epanechnikov(arg));\n\n                //orig weights\n                // Sbg += (q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j]) == 0) ? y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]) : 0;\n                // Sfg += q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j]);\n\n                //likelihood\n                Sbg += (wqi < wbi) ? y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]) : 0;\n                Sfg += q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j]);\n\n                //SCIA\n                // Sbg += (w == 0) ? y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]) : 0;\n                // Sfg += q_hist.getValue(Mi1[j], Mi2[j], Mi3[j]);\n\n                m0 += wg;\n                m1x += (j-cx)*wg;\n                m1y += (i-cy)*wg;\n            }\n        }\n\n        double xn_1 = m1x/m0 + cx;\n        double yn_1 = m1y/m0 + cy;\n\n        //Rebularization\n        double reg2 = 0, reg1 = 0;\n        reg1 = (wAvgBg - Sbg/Sfg);\n        if (std::abs(reg1) > bound1)\n            reg1 = reg1 > 0 ? bound1 : -bound1;\n\n        reg2 = -(log(h0));\n        if (std::abs(reg2) > bound2)\n            reg2 = reg2 > 0 ? bound2 : -bound2;\n\n        double h_tmp = (1.0 - wk_sum/m0)*h0 + (1.0/h0)*(wg_dist_sum/m0) + reg1 + reg2;\n\n        if (std::pow(xn_1 - cx,2) + std::pow(yn_1 - cy,2) < 0.1)\n            break;\n\n        if (m0==m0 && !std::isinf(m0) && m0 > 0){\n            cx = xn_1;\n            cy = yn_1;\n            h0 = 0.7*h0 + 0.3*h_tmp;\n            if (borderX > 5){\n                borderX /= 3;\n                borderY /= 3;\n            }\n        }else if (ii == 0){\n            //if in first iteration is m0 not valid => fail (maybe too fast movement)\n            //  try to enlarge the search region\n            borderX = 3*borderX;\n            borderY = 3*borderY;\n        }\n    }\n\n\n    *scale = h0;\n    if (iter != NULL)\n        *iter = ii;\n    return  cv::Point(cx, cy);\n}\n\ncv::Point ColorTracker::histMeanShiftAnisotropicScale(double x1, double y1, double x2, double y2, double * width, double * height)\n{\n    int maxIter = 15;\n\n    double w2 = (x2-x1)/2.;\n    double h2 = (y2-y1)/2.;\n\n    double borderX = 5.;\n    double borderY = 5.;\n\n    double cx = x1 + w2;\n    double cy = y1 + h2;\n    double h0_1 = 1.;\n    double h0_2 = 1.;\n\n    double wh;\n    double hh;\n\n    Histogram y1hist;\n\n    int ii;\n    for (ii = 0; ii < maxIter; ++ii){\n\n        wh = w2*h0_1+borderX;\n        hh = h2*h0_2+borderY;\n        int rowMin = std::max(0, (int)(cy-hh));\n        int rowMax = std::min(im1.rows-1, (int)(cy+hh));\n        int colMin = std::max(0, (int)(cx-wh));\n        int colMax = std::min(im1.cols-1, (int)(cx+wh));\n\n        extractForegroundHistogram(colMin, rowMin, colMax, rowMax, y1hist);\n\n        double batta_q = y1hist.computeSimilarity(&q_orig_hist);\n        double batta_b = y1hist.computeSimilarity(&b_hist);\n\n        //MeanShift Vector\n        double m0 = 0, m1x = 0, m1y = 0;\n        double Swigdist_1 = 0, Swigdist_2 = 0;\n        double wk_sum = 0, Sbg = 0, Sfg = 0;\n\n        for(int i = rowMin; i < rowMax; i++){\n            const uchar* Mi1 = im1.ptr<uchar>(i);\n            const uchar* Mi2 = im2.ptr<uchar>(i);\n            const uchar* Mi3 = im3.ptr<uchar>(i);\n            double tmp_y = std::pow((cy-(float)i)/hh,2);\n\n            for(int j = colMin; j < colMax; j++){\n                double arg = std::pow((cx-(float)j)/wh,2) + tmp_y;\n                if (arg>1)\n                    continue;\n\n                //likelihood weights\n                double wqi = sqrt(q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n                double wbi = sqrt(b_hist.getValue(Mi1[j], Mi2[j], Mi3[j])/y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]));\n                double w = std::max(wqi/batta_q - wbi/batta_b, 0.0);\n\n                double wg = w*(-kernelProfile_EpanechnikovDeriv(arg));\n\n                wk_sum += (w * kernelProfile_Epanechnikov(arg));\n                Swigdist_1 += wg*std::pow((cx-(float)j),2);\n                Swigdist_2 += wg*std::pow((cy-(float)i),2);\n\n                //likelihood\n                Sbg += (wqi < wbi) ? y1hist.getValue(Mi1[j], Mi2[j], Mi3[j]) : 0;\n                Sfg += q_orig_hist.getValue(Mi1[j], Mi2[j], Mi3[j]);\n\n                m0 += wg;\n                m1x += (j-cx)*wg;\n                m1y += (i-cy)*wg;\n            }\n        }\n\n        double a2 = std::pow(w2,2);\n        double b2 = std::pow(h2,2);\n\n        float mx = (h0_2/h0_1)*(m1x/m0);\n        float my = (h0_1/h0_2)*(m1y/m0);\n\n        double reg1 = (wAvgBg - Sbg/Sfg);\n        if (std::abs(reg1) > bound1)\n            reg1 = reg1 > 0 ? bound1 : -bound1;\n\n        double reg2_1 = -(log(h0_1));\n        if (std::abs(reg2_1) > bound2)\n            reg2_1 = reg2_1 > 0 ? bound2 : -bound2;\n        double reg2_2 = -(log(h0_2));\n        if (std::abs(reg2_2) > bound2)\n            reg2_2 = reg2_2 > 0 ? bound2 : -bound2;\n\n        double h_1 = h0_1 - (h0_2/2)*(wk_sum / m0) + (h0_2 / (h0_1 * h0_1 * a2)) * (Swigdist_1 / m0) + reg1 + reg2_1;\n        double h_2 = h0_2 - (h0_1/2)*(wk_sum / m0) + (h0_1 / (h0_2 * h0_2 * b2)) * (Swigdist_2 / m0) + reg1 + reg2_2;\n\n        if (std::pow(mx,2) + std::pow(my,2) < 0.1)\n            break;\n\n        if (m0==m0 && !std::isinf(m0) && m0 > 0){\n            cx = cx + mx;\n            cy = cy + my;\n            h0_1 = 0.7*h0_1 + 0.3*h_1;\n            h0_2 = 0.7*h0_2 + 0.3*h_2;\n            if (borderX > 5){\n                borderX /= 3;\n                borderY /= 3;\n            }\n        }else if (ii == 0){\n            //if in first iteration is m0 not valid => fail (maybe too fast movement)\n            //  try to enlarge the search region\n            borderX = 3*borderX;\n            borderY = 3*borderY;\n        }\n    }\n\n    *width = 2*w2*h0_1;\n    *height = 2*h2*h0_2;\n\n    return  cv::Point(cx, cy);\n}\n\n\n\nBBox * ColorTracker::track(cv::Mat & img, double x1, double y1, double x2, double y2)\n{\n    double width = x2-x1;\n    double height = y2-y1;\n\n    im1_old = im1.clone();\n    im2_old = im2.clone();\n    im3_old = im3.clone();\n    preprocessImage(img);\n\n    //MS with scale estimation\n    double scale = 1;\n    int iter = 0;\n    cv::Point modeCenter = histMeanShiftIsotropicScale(x1, y1, x2, y2, &scale, &iter);\n    width = 0.7*width + 0.3*width*scale;\n    height = 0.7*height + 0.3*height*scale;\n\n    //MS with anisotropic scale estimation\n    // double scale = 1.;\n    // double new_width = 1., new_height = 1.;\n    // cv::Point modeCenter = histMeanShiftAnisotropicScale(x1, y1, x2, y2, &new_width, &new_height);\n    // width = new_width;\n    // height = new_height;\n\n    //Forward-Backward validation\n    if (std::abs(std::log(scale)) > 0.05){\n        cv::Mat tmp_im1 = im1;\n        cv::Mat tmp_im2 = im2;\n        cv::Mat tmp_im3 = im3;\n        im1 = im1_old;\n        im2 = im2_old;\n        im3 = im3_old;\n        double scaleB = 1;\n        histMeanShiftIsotropicScale(modeCenter.x - width/2, modeCenter.y - height/2, modeCenter.x + width/2, modeCenter.y + height/2, &scaleB);\n        im1 = tmp_im1;\n        im2 = tmp_im2;\n        im3 = tmp_im3;\n\n        if (std::abs(std::log(scale*scaleB)) > 0.1){\n            double alfa = 0.1*(defaultWidth/(float)(x2-x1));\n            width = (0.9 - alfa)*(x2-x1) + 0.1*(x2-x1)*scale + alfa*defaultWidth;\n            height = (0.9 - alfa)*(y2-y1) + 0.1*(y2-y1)*scale + alfa*defaultHeight;\n        }\n    }\n\n    BBox * retBB = new BBox();\n    retBB->setBBox(modeCenter.x - width/2, modeCenter.y - height/2, width, height, 1, 1);\n    lastPosition.setBBox(modeCenter.x - width/2, modeCenter.y - height/2, width, height, 1, 1);\n    frame++;\n\n    return retBB;\n}\n\nvoid ColorTracker::preprocessImage(cv::Mat &img)\n{\n    cv::Mat ra[3] = {im1, im2, im3};\n    cv::split(img, ra);\n}\n\nvoid ColorTracker::extractBackgroundHistogram(int x1, int y1, int x2, int y2, Histogram & hist)\n{\n    int offsetX = (x2-x1)/2;\n    int offsetY = (y2-y1)/2;\n\n    int rowMin = std::max(0, (int)(y1-offsetY));\n    int rowMax = std::min(im1.rows, (int)(y2+offsetY+1));\n    int colMin = std::max(0, (int)(x1-offsetX));\n    int colMax = std::min(im1.cols, (int)(x2+offsetX+1));\n\n    int numData = (rowMax-rowMin)*(colMax-colMin) - (y2-y1)*(x2-x1);\n\n    if (numData < 1)\n        numData = (rowMax-rowMin)*(colMax-colMin)/2 + 1;\n\n    std::vector<int> d1, d2, d3;\n    std::vector<double> weights;\n    d1.reserve(numData);\n    d2.reserve(numData);\n    d3.reserve(numData);\n\n    for (int y = rowMin; y < rowMax; ++y){\n        const uchar * M1 = im1.ptr<uchar>(y);\n        const uchar * M2 = im2.ptr<uchar>(y);\n        const uchar * M3 = im3.ptr<uchar>(y);\n        for (int x = colMin; x < colMax; ++x){\n            if (x >= x1 && x <= x2 && y >= y1 && y <= y2)\n                continue;\n            d1.push_back(M1[x]);\n            d2.push_back(M2[x]);\n            d3.push_back(M3[x]);\n        }\n    }\n    hist.clear();\n    hist.insertValues(d1, d2, d3, weights);\n}\n\n\nvoid ColorTracker::extractForegroundHistogram(int x1, int y1, int x2, int y2, Histogram & hist)\n{\n    hist.clear();\n    std::vector<int> data1;\n    std::vector<int> data2;\n    std::vector<int> data3;\n    std::vector<double> weights;\n\n    int numData = (y2-y1)*(x2-x1);\n\n    if (numData <= 0){\n        return;\n    }\n\n    data1.reserve(numData);\n    data2.reserve(numData);\n    data3.reserve(numData);\n    weights.reserve(numData);\n\n    double w2 = (x2-x1)/2;\n    double h2 = (y2-y1)/2;\n\n    double cx = x1 + w2;\n    double cy = y1 + h2;\n\n    double wh_i = 1.0/(w2*1.4142+1);  //sqrt(2)\n    double hh_i = 1.0/(h2*1.4142+1);\n\n    for (int y = y1; y < y2+1; ++y){\n        const uchar * M1 = im1.ptr<uchar>(y);\n        const uchar * M2 = im2.ptr<uchar>(y);\n        const uchar * M3 = im3.ptr<uchar>(y);\n        double tmp_y = std::pow((cy-y)*hh_i,2);\n        for (int x = x1; x < x2+1; ++x){\n            data1.push_back(M1[x]);\n            data2.push_back(M2[x]);\n            data3.push_back(M3[x]);\n            weights.push_back(kernelProfile_Epanechnikov(std::pow((cx-x)*wh_i,2) + tmp_y));\n        }\n    }\n\n\n    hist.clear();\n    hist.insertValues(data1, data2, data3, weights);\n}\n"
  },
  {
    "path": "src/colotracker.h",
    "content": "#ifndef COLOTRACKER_H\n#define COLOTRACKER_H\n\n#include \"cv.h\"\n#include \"highgui.h\"\n#include \"region.h\"\n#include \"histogram.h\"\n#include <iostream>\n\n\n//#define SHOWDEBUGWIN\n\n#define BIN_1 16\n#define BIN_2 16\n#define BIN_3 16\n\nclass ColorTracker\n{\nprivate:\n    BBox lastPosition;\n    cv::Mat im1;\n    cv::Mat im2;\n    cv::Mat im3;\n\n    cv::Mat im1_old;\n    cv::Mat im2_old;\n    cv::Mat im3_old;\n\n    Histogram q_hist;\n    Histogram q_orig_hist;\n    Histogram b_hist;\n\n    double defaultWidth;\n    double defaultHeight;\n\n    double wAvgBg;\n    double bound1;\n    double bound2;\n\n    cv::Point histMeanShift(double x1, double y1, double x2, double y2);\n    cv::Point histMeanShiftIsotropicScale(double x1, double y1, double x2, double y2, double * scale, int * msIter = NULL);\n    cv::Point histMeanShiftAnisotropicScale(double x1, double y1, double x2, double y2, double * width, double * height);\n\n    void preprocessImage(cv::Mat & img);\n    void extractBackgroundHistogram(int x1, int y1, int x2, int y2, Histogram &hist);\n    void extractForegroundHistogram(int x1, int y1, int x2, int y2, Histogram &hist);\n\n    inline double kernelProfile_Epanechnikov(double x)\n        { return (x <= 1) ? (2.0/3.14)*(1-x) : 0; }\n        //{ return (x <= 1) ? (1-x) : 0; }\n    inline double kernelProfile_EpanechnikovDeriv(double x)\n        { return (x <= 1) ? (-2.0/3.14) : 0; }\n        //{ return (x <= 1) ? -1 : 0; }\npublic:\n    int frame;\n    int sumIter;\n\n\t// Init methods\n    void init(cv::Mat & img, int x1, int y1, int x2, int y2);\n\n    // Set last object position - starting position for next tracking step\n    inline void setLastBBox(int x1, int y1, int x2, int y2)\n\t{\n        lastPosition.setBBox(x1, y1, x2-x1, y2-y1, 1, 1);\n    }\n\n    inline BBox * getBBox()\n    {\n        BBox * bbox = new BBox();\n        *bbox = lastPosition;\n        return bbox;\n    }\n\n\t// frame-to-frame object tracking\n    BBox * track(cv::Mat & img, double x1, double y1, double x2, double y2);\n    inline BBox * track(cv::Mat & img)\n    {\n        return track(img, lastPosition.x, lastPosition.y, lastPosition.x + lastPosition.width, lastPosition.y + lastPosition.height);\n    }\n};\n\n#endif // COLOTRACKER_H\n"
  },
  {
    "path": "src/histogram.cpp",
    "content": "#include \"histogram.h\"\n#include <cstring>\n#include <assert.h>\n#include <iostream>\n#include <algorithm>\n#include <numeric>\n#include <cmath>\n#include <fstream>\n\nHistogram::Histogram(int _dimSize, int _range)\n{\n    dimSize = _dimSize;\n    range = _range;\n}\n\nvoid Histogram::insertValues(std::vector<int> & data1, std::vector<int> & data2, std::vector<int> & data3, std::vector<double> &weight)\n{\n    if (data.size() < (unsigned int)dimSize*dimSize*dimSize)\n        data.resize(dimSize*dimSize*dimSize);\n\n    bool useWeights = true;\n    if (weight.size() != data1.size())\n        useWeights = false;\n\n    rangePerBin = range/dimSize;\n    rangePerBinInv = 1./(float)rangePerBin;\n    double sum = 0;\n    for (unsigned int i=0; i < data1.size(); ++i){\n        int id1 = rangePerBinInv*data1[i];\n        int id2 = rangePerBinInv*data2[i];\n        int id3 = rangePerBinInv*data3[i];\n        int id = id1*dimSize*dimSize + id2*dimSize + id3;\n\n        double w = useWeights ? weight[i] : 1;\n\n        data[id] += w;\n        sum += w;\n    }\n\n    normalize();\n}\n\ndouble Histogram::computeSimilarity(Histogram * hist)\n{\n    double conf = 0;\n    for (unsigned int i=0; i < data.size(); ++i) {\n        conf += sqrt(data[i]*hist->data[i]);\n    }\n    return conf;\n}\n\ndouble Histogram::getValue(int val1, int val2, int val3)\n{\n    int id1 = rangePerBinInv*val1;\n    int id2 = rangePerBinInv*val2;\n    int id3 = rangePerBinInv*val3;\n    int id = id1*dimSize*dimSize + id2*dimSize + id3;\n    return data[id];\n}\n\nvoid Histogram::transformToWeights()\n{\n    double min = 0;\n/*    std::ifstream alfa_file;\n    alfa_file.open(\"param.txt\");\n    if (alfa_file.is_open()){\n        double sum = 0;\n        for (unsigned int i=0; i < data.size(); ++i) {\n            sum += data[i];\n        }\n        double alfa;\n        alfa_file >> alfa;\n        min = (alfa/100.0)*sum;\n        alfa_file.close();\n    }else*/\n        min = getMin();\n\n    transformByWeight(min);\n}\n\nvoid Histogram::transformByWeight(double min)\n{\n    for (unsigned int i=0; i < data.size(); ++i){\n        if (data[i] > 0){\n            data[i] = min/data[i];\n            if (data[i] > 1)\n                data[i] = 1;\n        }else\n            data[i] = 1;\n    }\n\n}\n\nvoid Histogram::multiplyByWeights(Histogram * hist)\n{\n    double sum = 0;\n    for (unsigned int i=0; i < data.size(); ++i) {\n        data[i] *= hist->data[i];\n        sum += data[i];\n    }\n\n    normalize();\n}\nvoid Histogram::clear()\n{\n    for (unsigned int i=0; i < data.size(); ++i)\n        data[i] = 0;\n}\n\nvoid Histogram::normalize()\n{\n    double sum = 0;\n    for (unsigned int i=0; i < data.size(); ++i)\n        sum += data[i];\n    for (unsigned int i=0; i < data.size(); ++i)\n        data[i] /= sum;\n}\n\ndouble Histogram::getMin()\n{\n    double min = 1;\n    for (unsigned int i=0; i < data.size(); ++i) {\n        if (data[i] < min && data[i] != 0)\n            min = data[i];\n    }\n    return min;\n}\n\nvoid Histogram::addExpHist(double alpha, Histogram & hist)\n{\n    double beta = 1-alpha;\n    for (unsigned int i=0; i < data.size(); ++i){\n        data[i] = beta*data[i] + alpha*hist.data[i];\n    }\n}\n\n"
  },
  {
    "path": "src/histogram.h",
    "content": "#ifndef HISTOGRAM_H\n#define HISTOGRAM_H\n\n#include <vector>\n#include <numeric>\n\nclass Bin;\n\nclass Histogram\n{\npublic:\n    std::vector<double> data;\n    int dimSize;\n    int range;\n    int rangePerBin;\n    float rangePerBinInv;\n    bool initialized;\n\n    Histogram(int _dimSize = 16, int _range = 256);\n    ~Histogram(){}\n\n    void insertValues(std::vector<int> & data1,\n                      std::vector<int> & data2,\n                      std::vector<int> & data3,\n                      std::vector<double> & weight);\n\n    double computeSimilarity(Histogram * hist);\n\n    double getValue(int val1, int val2, int val3);\n    void addValue(int val1, int val2, int val3, double weight);\n\n    double getMin();\n    void transformToWeights();\n    void transformByWeight(double min);\n    void multiplyByWeights(Histogram * hist);\n\n    void clear();\n    void normalize();\n    void addExpHist(double alpha, Histogram & hist);\n\n};\n\n#endif //HISTOGRAM_H\n"
  },
  {
    "path": "src/region.cpp",
    "content": "///////////////////////////////////////////////////////////\n//  region.cpp\n//  Implementation of the Class BBox\n//  Created on:      15-II-2010 18:00:19\n//  Original author: Tomas Vojir\n///////////////////////////////////////////////////////////\n\n#include \"region.h\"\n#include <algorithm>\n\nvoid BBox::setBBox(double _x, double _y, double _width, double _height, double _accuracy, double _normCross){\n\taccuracy = _accuracy;\n\theight = _height;\n\twidth = _width;\n\tx = _x;\n\ty = _y;\n    normCross = _normCross;\n}\n\n\ndouble * BBox::getTopLeftWidthHeight()\n{\n    double * ret = new double[4];\n    ret[0] = x;\n    ret[1] = y;\n    ret[2] = width;\n    ret[3] = height;\n\n    return ret;\n}\n\nstd::vector<BBox *> BBox::bbOverlap(std::vector<BBox *> & vect, double overLap)\n{\n    std::vector<BBox *> ret;\n    std::vector<BBox *> retP;\n    double x1, y1;\n    BBox * tmp;\n    double intersection;\n    double over = overLap;\n\n    if (over == 0)\n        over = 0.7;\n\n    for(std::vector<BBox *>::iterator it = vect.begin(); it != vect.end(); ++it){\n        tmp = *it;\n        x1 =  std::min(x + width, tmp->x + tmp->width) - std::max(x, tmp->x) + 1;\n        if (x1 <= 0)\n        {\n            ret.push_back(*it);\n            continue;\n        }\n\n        y1 =  std::min(y + height, tmp->y + tmp->height) - std::max(y, tmp->y) + 1;\n\n        if (y1 <= 0)\n        {\n            ret.push_back(*it);\n            continue;\n        }\n\n        intersection = x1 * y1;\n\n        if ( (intersection / (width * height + tmp->width * tmp->height - intersection)) >= over)\n            retP.push_back(*it);\n        else\n            ret.push_back(*it);\n    }\n    vect = retP;\n\n    return (ret);\n}\n\ndouble BBox::bbOverlap(BBox * tmp)\n{\n    double x1, y1;\n    double intersection;\n\n    x1 =  std::min(x + width, tmp->x + tmp->width) - std::max(x, tmp->x) + 1;\n    if (x1 <= 0)\n        return 0;\n    y1 =  std::min(y + height, tmp->y + tmp->height) - std::max(y, tmp->y) + 1;\n\n    if (y1 <= 0)\n        return 0;\n\n    intersection = x1 * y1;\n\tdouble area1 = (width+1)*(height+1);\n\tdouble area2 = (tmp->width+1)*(tmp->height+1);\n    return (intersection / (area1 + area2 - intersection));\n}\n\ndouble BBox::bbCoverage(BBox * tmp)\n{\n    double x1, y1;\n    double intersection;\n\n    x1 =  std::min(x + width, tmp->x + tmp->width) - std::max(x, tmp->x) + 1;\n    if (x1 <= 0)\n        return 0;\n    y1 =  std::min(y + height, tmp->y + tmp->height) - std::max(y, tmp->y) + 1;\n\n    if (y1 <= 0)\n        return 0;\n\n    intersection = x1 * y1;\n\n    return (intersection);\n\n}\n\nstd::vector<BBox *> BBox::clusterBBoxes(std::vector<BBox *> & BB)\n{\n    std::vector<BBox *> ret;\n    std::vector<BBox *> tmpV1;\n    std::vector<BBox *> tmpV2;\n    std::vector<BBox *> tmpV3;\n\n    BBox * tmp;\n\n    if (BB.size() == 0)\n        return ret;\n\n    while(1){\n        tmp = BB[0];\n        tmpV1 = tmp->bbOverlap(BB);\n        tmpV3 = BB;\n\n        for (std::vector<BBox *>::iterator it = BB.begin(); it != BB.end(); ++it){\n            tmpV2 = (*it)->bbOverlap(tmpV1);\n            for (unsigned int i=0; i<tmpV1.size(); ++i)\n                tmpV3.push_back(tmpV1[i]);\n            tmpV1.swap(tmpV2);\n        }\n\n        if (tmpV3.size() != 0){\n            BBox * bbox = new BBox();\n            bbox->setBBox(0,0,0,0,0);\n            bbox->normCross = 0;\n            for (std::vector<BBox *>::iterator it = tmpV3.begin(); it != tmpV3.end(); ++it){\n                bbox->x += (*it)->x;\n                bbox->y += (*it)->y;\n                bbox->width += (*it)->width;\n                bbox->height += (*it)->height;\n                if ((*it)->normCross > bbox->normCross)\n                    bbox->normCross = (*it)->normCross;\n                if ((*it)->accuracy > bbox->accuracy)\n                    bbox->accuracy = (*it)->accuracy;\n                delete *it;\n            }\n            bbox->x /= tmpV3.size();\n            bbox->y /= tmpV3.size();\n            bbox->width /= tmpV3.size();\n            bbox->height /= tmpV3.size();\n\n            ret.push_back(bbox);\n            tmpV3.clear();\n            tmpV2.clear();\n        }\n        BB.swap(tmpV1);\n\n        if (BB.size() == 0)\n            break;\n    }\n\n    return ret;\n}\n\nstd::vector<BBox *> BBox::findDiff(std::vector<BBox *> & A, std::vector<BBox *> & B)\n{\n    bool equal;\n    std::vector<BBox *> ret;\n    if (B.size()==0){\n        ret = A;\n        return (ret);\n    }\n\n    for(std::vector<BBox *>::iterator it = A.begin(); it != A.end(); ++it){\n        equal = false;\n        for (std::vector<BBox *>::iterator it2 = B.begin(); it2 != B.end(); ++it2)\n            if (*it == *it2) {\n                equal = true;\n                break;\n            }\n\n        if (!equal)\n            ret.push_back(*it);\n    }\n\n    return (ret);\n}\n\n\n"
  },
  {
    "path": "src/region.h",
    "content": "///////////////////////////////////////////////////////////\n//  region.h\n//  Implementation of the Class BBox\n//  Created on:      15-II-2010 18:00:18\n//  Original author: Tomas Vojir\n///////////////////////////////////////////////////////////\n\n#if !defined(BBOX_C3F6FA38_1DD3_4796_8FB6_62A80862095F__INCLUDED_)\n#define BBOX_C3F6FA38_1DD3_4796_8FB6_62A80862095F__INCLUDED_\n\n#include <vector>\n\n\nclass BBox\n{\n\npublic:\n    BBox() {\n\n    }\n    ~BBox() {\n\n    }\n\n    double accuracy;\n    double normCross;\n    double height;\n    double width;\n    double x;\n    double y;\n\n    double * getTopLeftWidthHeight();\n    void setBBox(double _x, double _y, double _width, double _height, double _accuracy, double _normCross = 0);\n    std::vector<BBox *> bbOverlap(std::vector<BBox *> & vect, double overLap = 0.0);\n    double bbOverlap(BBox * b_box);\n    double bbCoverage(BBox * tmp);\n    static std::vector<BBox *> clusterBBoxes(std::vector<BBox *> & BB);\n    static std::vector<BBox *> findDiff(std::vector<BBox *> & A, std::vector<BBox *> & B);\n\n\n    bool operator==(const BBox & right) const\n    {\n        if ( (this->x - right.x) != 0  ||\n             (this->y - right.y) != 1  ||\n             this->width != right.width ||\n             this->height != right.height )\n            return false;\n        return true;\n    }\n\n    bool operator!=(const BBox & right) const\n    {\n        return !(*this == right);\n    }\n\n};\n\n\n\n#endif // !defined(BBOX_C3F6FA38_1DD3_4796_8FB6_62A80862095F__INCLUDED_)\n"
  },
  {
    "path": "vot.hpp",
    "content": "/* \n *  Author : Tomas Vojir\n *  Date   : 2013-06-05\n *  Desc   : Simple class for parsing VOT inputs and providing \n *           interface for image loading and storing output.\n */ \n\n#ifndef CPP_VOT_H\n#define CPP_VOT_H\n\n#include <string>\n#include <fstream>\n#include <iostream>\n#include <opencv2/opencv.hpp>\n\n\n// Bounding box type\n/* format:\n    2 3\n    1 4\n*/\ntypedef struct {\n    float x1;   //bottom left\n    float y1;\n    float x2;   //top left\n    float y2;\n    float x3;   //top right\n    float y3;\n    float x4;   //bottom right\n    float y4;\n} VOTPolygon;\n\nclass VOT\n{\npublic:\n    VOT(const std::string & region_file, const std::string & images, const std::string & ouput)\n    {\n        _images = images;\n        p_region_stream.open(region_file.c_str());\n        VOTPolygon p;\n        if (p_region_stream.is_open()){\n            std::string line;\n            std::getline(p_region_stream, line);\n            std::vector<float> numbers;\n            std::istringstream s( line );\n            float x;\n            char ch;\n            while (s >> x){\n                numbers.push_back(x);\n                s >> ch;\n            }\n            if (numbers.size() == 4) {\n                float x = numbers[0], y = numbers[1], w = numbers[2], h = numbers[3];\n                p.x1 = x;\n                p.y1 = y + h;\n                p.x2 = x;\n                p.y2 = y;\n                p.x3 = x + w;\n                p.y3 = y;\n                p.x4 = x + w;\n                p.y4 = y + h;\n            } else if (numbers.size() == 8) {\n                p.x1 = numbers[0];\n                p.y1 = numbers[1];\n                p.x2 = numbers[2];\n                p.y2 = numbers[3];\n                p.x3 = numbers[4];\n                p.y3 = numbers[5];\n                p.x4 = numbers[6];\n                p.y4 = numbers[7];\n            } else {\n                std::cerr << \"Error loading initial region in file - unknow format \" << region_file << \"!\" << std::endl;\n                p.x1=0;\n                p.y1=0;\n                p.x2=0;\n                p.y2=0;\n                p.x3=0;\n                p.y3=0;\n                p.x4=0;\n                p.y4=0;\n            }\n        }else{\n            std::cerr << \"Error loading initial region in file \" << region_file << \"!\" << std::endl;\n            p.x1=0;\n            p.y1=0;\n            p.x2=0;\n            p.y2=0;\n            p.x3=0;\n            p.y3=0;\n            p.x4=0;\n            p.y4=0;\n        }\n\n        p_init_polygon = p;\n\n        p_images_stream.open(images.c_str());\n        if (!p_images_stream.is_open())\n            std::cerr << \"Error loading image file \" << images << \"!\" << std::endl;\n\n        p_output_stream.open(ouput.c_str());\n        if (!p_output_stream.is_open())\n            std::cerr << \"Error opening output file \" << ouput << \"!\" << std::endl;\n    }\n\n    ~VOT()\n    {\n        p_region_stream.close();\n        p_images_stream.close();\n        p_output_stream.close();\n    }\n\n    inline cv::Rect getInitRectangle() const \n    {   \n        // read init box from ground truth file\n        VOTPolygon initPolygon = getInitPolygon();\n        float x1 = std::min(initPolygon.x1, std::min(initPolygon.x2, std::min(initPolygon.x3, initPolygon.x4)));\n        float x2 = std::max(initPolygon.x1, std::max(initPolygon.x2, std::max(initPolygon.x3, initPolygon.x4)));\n        float y1 = std::min(initPolygon.y1, std::min(initPolygon.y2, std::min(initPolygon.y3, initPolygon.y4)));\n        float y2 = std::max(initPolygon.y1, std::max(initPolygon.y2, std::max(initPolygon.y3, initPolygon.y4)));\n        return cv::Rect(x1, y1, x2-x1, y2-y1);\n    }\n\n    inline VOTPolygon getInitPolygon() const \n    {   return p_init_polygon;    }\n\n\n    inline void outputBoundingBox(const cv::Rect & bbox)\n    {\n        p_output_stream << bbox.x << \",\" << bbox.y << \",\";\n        p_output_stream << bbox.width << \",\" << bbox.height << std::endl;\n    }\n\n    inline void outputPolygon(const VOTPolygon & poly)\n    {\n      p_output_stream << poly.x1 << \",\" << poly.y1 << \",\";\n      p_output_stream << poly.x2 << \",\" << poly.y2 << \",\";\n      p_output_stream << poly.x3 << \",\" << poly.y3 << \",\";\n      p_output_stream << poly.x4 << \",\" << poly.y4 << std::endl;\n    }\n\n    inline int getNextFileName(char * fName)\n    {\n        if (p_images_stream.eof() || !p_images_stream.is_open())\n            return -1;\n        std::string line;\n        std::getline (p_images_stream, line);\n        strcpy(fName, line.c_str());\n        return 1;\n    }\n\n    inline int getNextImage(cv::Mat & img)\n    {\n        if (p_images_stream.eof() || !p_images_stream.is_open())\n                return -1;\n\n        std::string line;\n        std::getline (p_images_stream, line);\n    \tif (line.empty() && p_images_stream.eof()) return -1;\n        img = cv::imread(line, CV_LOAD_IMAGE_COLOR);\n     \n        return 1;\n    }\n\nprivate:\n    std::string _images;\n    VOTPolygon p_init_polygon;\n    std::ifstream p_region_stream;\n    std::ifstream p_images_stream;\n    std::ofstream p_output_stream;\n\n};\n\n#endif //CPP_VOT_H\n"
  }
]