1. 程式人生 > >一個基於OCV的人肉選取特徵點程式

一個基於OCV的人肉選取特徵點程式

基於OpenCV寫了一個互動式獲取圖片上的人肉選取的特徵,並儲存到檔案的小程式。
典型應用場景:當在一個精度不高的應用需求中,相機解析度差或者變形嚴重,某些棋盤點通過程式碼檢測不出,就可以通過手工選取的方式。

使用

  • 通過介面的trackbar來縮放圖片
  • 單擊右鍵設定顯示中心點
  • 單擊左鍵選取並記錄點
  • '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):key 'c' to remove the last picked point
4):key 'q' to quit the program and save point
5):use the "default size scale" to adjust the display size
*/

using namespace cv;
using namespace std;


const bool using_fix_param = true;


const string WIN_NAME = "Pick_Point";
const string SCALE = "scale";
int scale;              //trackbar的值

string data_save_path;  //資料儲存路徑
string PIC_PATH;        //圖片路徑
Mat srcImg;             //原始圖片
Mat curImg;             //當前顯示的圖片
Size srcImgSize;        //原始圖片大小
Size winSize;           //顯示視窗大小
float curScale;         //當前的縮放比例
Point2i curShowCenter;  //當前顯示的影象相對於srcImg偏移的座標
Point2i showRange;      //顯示的圖片範圍,與curShowCenter共同組成了圖片的顯示範圍
int maxScale;           //縮放的最大比例
int minScale;           //縮放的最小比例


vector<Point2f> choosePoints;   //通過本程式選取的點

void showdata() {
    if (false) {
        cout << ">>>>>>>>>>>>>>>>>>>>>>>>\n";
        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 << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n";
    }
}

//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 << "\n";
        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\n";
        exit(0);
    }
}


void on_scale(int value, void *data) {
    showdata();

    if (scale > maxScale) {
        scale = maxScale;
    }
    if (scale < minScale) {
        scale = minScale;
    }
    curScale = scale / 10.0;

    showRange.x = winSize.width / curScale;
    showRange.y = winSize.height / curScale;

    showimg();
}


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\n";
        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_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;
    default:
        break;
    }
}


int main() {


    if (using_fix_param) {
        curScale = 0.3;
    }
    else {
        cout << "please input the default size scale:";
        cin >> curScale;
    }

    if (curScale < 0.1) curScale = 0.1;
    if (curScale > 10) curScale = 10;
    scale = (int)(curScale * 10);
    curScale = scale / 10.0;    //trackbar的值除以10即為縮放比例
    maxScale = 100;
    minScale = scale;

    if (using_fix_param)
    {
        PIC_PATH = "E:\\project\\Car360\\Car360\\HomoToG\\undistort_head.jpg";
    }
    else {
        cout << "\npicture path:";
        cin >> PIC_PATH;
    }


    if (using_fix_param)
    {
        data_save_path = "data.txt";
    } 
    else
    {
        cout << "\ndata 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);
    createTrackbar(SCALE, WIN_NAME, &scale, 100, on_scale);
    setMouseCallback(WIN_NAME, on_mouse);

    showimg();

    waitKey();
    return 0;
}