zoukankan      html  css  js  c++  java
  • 一个基于OCV的人肉选取特征点程序

    基于OpenCV写了一个交互式获取图片上的人肉选取的特征,并保存到文件的小程序。
    典型应用场景:当在一个精度不高的应用需求中,相机分辨率差或者变形严重,某些棋盘点通过代码检测不出,就可以通过手工选取的方式。

    使用

    • 通过滚轮来缩放图片显示
    • 单击右键设置显示中心点
    • 单击左键选取并记录点
    • 'c'来取消上一次取点
    • 'q'退出并保存数据

    界面

    F7avYn.png

    代码

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <stdlib.h>
    
    
    /*
    use guide:
    1):left button click to pick one point;
    2):right button click to set as the center to display;
    3):wheel to zoom the image
    4):key 'c' to remove the last picked point
    5):key 'q' to quit the program and save point
    6):use the "default size scale" to adjust the display size
    */
    
    using namespace cv;
    using namespace std;
    
    
    const bool using_fix_param = true;
    
    const float SCALE_STEP = 0.1;
    const string WIN_NAME = "Pick_Point";
    
    string data_save_path;  //数据保存路径
    string PIC_PATH;        //图片路径
    Mat srcImg;             //原始图片
    Mat curImg;             //当前显示的图片
    Size srcImgSize;        //原始图片大小
    Size winSize;           //显示窗口大小
    float curScale;         //当前的缩放比例
    Point2i curShowCenter;  //当前显示的图像相对于srcImg偏移的坐标
    Point2i showRange;      //显示的图片范围,与curShowCenter共同组成了图片的显示范围
    float minScale;         //缩放的最小比例
    
    
    vector<Point2f> choosePoints;   //通过本程序选取的点
    
    void showdata() {
        if (false) {
            cout << ">>>>>>>>>>>>>>>>>>>>>>>>
    ";
            cout << "curScale:" << curScale << endl;
            cout << "curShowCenter:" << curShowCenter.x << "*" << curShowCenter.y << endl;
            cout << "showRange:" << showRange.x << "*" << showRange.y << endl;
            cout << "winSize:" << winSize.width << "*" << winSize.height << endl;
            cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ";
        }
    }
    
    //define the save-format for yourself during this function
    void saveAndQuit(vector<Point2f> pts) {
        const int POINT_PER_LINE = 4;
    
        FILE *stream = fopen(data_save_path.c_str(), "w");
        for (int i = 0; i < pts.size(); )
        {
            stringstream ss;
            for (int j = 0; j < POINT_PER_LINE && i < pts.size(); ++j, ++i)
            {
                //ss << pts[j].x << ", " << pts[j].y << ", ";
                ss << "Point2f(" << pts[i].x << ", " << pts[i].y << "), ";
            }
            ss << "
    ";
            string msg = ss.str();
            if (i == pts.size()) {
                msg = msg.substr(0, msg.length() - 3);
            }
            fwrite(msg.c_str(), msg.length(), 1, stream);
            cout << msg << endl;
        }
        fflush(stream);
        fclose(stream);
    }
    
    void keepCenterValid() {
        int minCenterX = winSize.width / curScale / 2;
        int minCenterY = winSize.height / curScale / 2;
        int maxCenterX = srcImgSize.width - minCenterX;
        int maxCenterY = srcImgSize.height - minCenterY;
    
        if (curShowCenter.x < minCenterX) curShowCenter.x = minCenterX;
        if (curShowCenter.x > maxCenterX) curShowCenter.x = maxCenterX;
        if (curShowCenter.y < minCenterY) curShowCenter.y = minCenterY;
        if (curShowCenter.y > maxCenterY) curShowCenter.y = maxCenterY;
    }
    
    void showimg() {
        showdata();
    
        Mat pts = srcImg.clone();
        drawChessboardCorners(pts, Size(11, 11), choosePoints, false);    //size可以随便写,只要后面为false即可
    
        int left = curShowCenter.x - showRange.x / 2;
        if (left < 0) left = 0;
        int right = curShowCenter.x + showRange.x / 2;
        if (right > pts.cols) right = pts.cols;
        int top = curShowCenter.y - showRange.y / 2;
        if (top < 0) top = 0;
        int bottom = curShowCenter.y + showRange.y / 2;
        if (bottom > pts.rows) bottom = pts.rows;
        curImg = pts.colRange(left, right).rowRange(top, bottom).clone();
    
        //cout << curImg.cols << " " << showRange.x << " " << curImg.rows << " " << showRange.y << endl;
    
        Mat scale_img;
        resize(curImg, scale_img, winSize);
    
        imshow(WIN_NAME, scale_img);
    
        void removeLastPoint();
        int key = waitKey();
        if (key == 'c') {
            removeLastPoint();
        }
        else if(key == 'q'){
            saveAndQuit(choosePoints);
            cout << "save and quit
    ";
            exit(0);
        }
    }
    
    
    void addPoint(int x, int y) {
        //使用这个可以保证计算精度,直接使用curScale可能由于前面计算的取整问题而导致精度问题,对于原图尺寸较大且放大倍数也大时,这个问题会变的比较明显
        const float scaleX = winSize.width / (float)curImg.cols;
        const float scaley = winSize.height / (float)curImg.rows;
        //cout << "add point scale "  << curImg.cols << " " << scaleX << " " << scaley << " " << curScale << endl;
        //使用这种方式,(curShowCenter.x - showRange.x / 2),可以保持和imshow的时候一致,避免出现精度问题
        float picx = (curShowCenter.x - showRange.x / 2) + x / scaleX;
        float picy = (curShowCenter.y - showRange.y / 2) + y / scaley;
        choosePoints.push_back(Point2f(picx, picy));
        cout << ">>>>add:" << picx << " " << picy << endl;
        showimg();
    }
    
    void removeLastPoint() {
        if (choosePoints.size() > 0) {
            choosePoints.erase(choosePoints.end() - 1);
            cout << "remove
    ";
            showimg();
        }
    }
    
    void setShowCenter(int x, int y) {
    
        curShowCenter.x += (x - winSize.width / 2) / curScale;
        curShowCenter.y += (y - winSize.height / 2) / curScale;
    
        keepCenterValid();
        showimg();
    }
    
    void on_whellScaleEvent(int flags) {
        //返回值为120的倍数。120表示滚动了一格。大于0表示向前,小于0表示向后
        int v = getMouseWheelDelta(flags) / 120;
    
        showdata();
    
        curScale += (v * SCALE_STEP);
        if (curScale < minScale) curScale = minScale;
    
        showRange.x = winSize.width / curScale;
        showRange.y = winSize.height / curScale;
    
        showimg();
    }
    
    void on_mouse(int event, int x, int y, int flags, void* userdata) {
        switch (event)
        {
        case CV_EVENT_RBUTTONDOWN:  //右键,设定显示中心
            setShowCenter(x, y);
            break;
        case CV_EVENT_LBUTTONDOWN:  //左键单击,选取点
            addPoint(x, y);
            break;
        case CV_EVENT_MOUSEWHEEL:
            on_whellScaleEvent(flags);
            break;
        default:
            break;
        }
    }
    
    
    int main() {
    
    
        if (using_fix_param) {
            curScale = 0.3;
        }
        else {
            cout << "please input the default size scale:";
            cin >> curScale;
        }
    
        minScale = curScale;    //以用户输入的scale为最小值,这个是刚好最小的倍数能填满整个win
    
        if (using_fix_param)
        {
            PIC_PATH = "sample.jpg";
        }
        else {
            cout << "
    picture path:";
            cin >> PIC_PATH;
        }
    
    
        if (using_fix_param)
        {
            data_save_path = "data.txt";
        } 
        else
        {
            cout << "
    data save path:";
            cin >> data_save_path;
        }
        
    
        srcImg = imread(PIC_PATH, 1);
        srcImgSize = srcImg.size();
        curShowCenter = srcImgSize / 2;
    
        winSize = Size(srcImgSize.width  * curScale, srcImgSize.height * curScale);
        showRange.x = winSize.width / curScale;
        showRange.y = winSize.height / curScale;
    
        namedWindow(WIN_NAME);
        setMouseCallback(WIN_NAME, on_mouse);
    
        showimg();
    
        waitKey();
        return 0;
    }
    
  • 相关阅读:
    HDU2438:Turn the corner(三分)
    XTU1267:Highway(LCA+树的直径)
    HDU6024:Building Shops(DP)
    “玲珑杯”ACM比赛 Round #13 B -- 我也不是B(二分排序)
    XTU1266:Parentheses(贪心+优先队列)
    Educational Codeforces Round 21 D
    Educational Codeforces Round 21E selling souvenirs (dp)
    EOJ3247:铁路修复计划
    关于工厂模式的 个人理解
    设计模式之 工厂方法
  • 原文地址:https://www.cnblogs.com/willhua/p/10225937.html
Copyright © 2011-2022 走看看