基于OpenCV写了一个交互式获取图片上的人肉选取的特征,并保存到文件的小程序。

典型应用场景:当在一个精度不高的应用需求中,相机分辨率差或者变形严重,某些棋盘点通过代码检测不出,就可以通过手工选取的方式。

使用

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

界面

代码

#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 << ">>>>>>>>>>>>>>>>>>>>>>>>\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 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_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 << "\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);
setMouseCallback(WIN_NAME, on_mouse); showimg(); waitKey();
return 0;
}

一个基于OCV的人肉选取特征点程序的更多相关文章

  1. Xamarin 小试牛刀 通知栏消息通知和按钮(基于Java代码人肉转换)

    本示例基于网友现有安卓项目人肉翻译,在Xamarin中替换和修改了很多方法的命名,比如某些属性需要去掉getName的get前缀, 有些方法名称需要使用Pascal命名法替换Java的Camel 命名 ...

  2. 经验分享:一个 30 岁的人是如何转行做程序员,进入IT行业的?

    大约一年以前,我成为了一名全职开发者,我想要总结一下这一年的经验,并且和所有人分享,一个 30 多岁的人是如何进入科技行业的: 改变职业是一件吓人的事情,有时候还会成为一件危险的事情.年龄越大,危险就 ...

  3. eShopOnContainers 是一个基于微服务的.NET Core示例框架

    找到一个好的示例框架很难,但不是不可能.大多数是小型Todo风格的应用程序,通常基于SimpleCRUD.值得庆幸的是,Microsoft已经为eShopOnContainers创建了一个基于微服务的 ...

  4. 在Ubuntu上部署一个基于webrtc的多人视频聊天服务

    最近研究webrtc视频直播技术,网上找了些教程最终都不太能顺利跑起来的,可能是文章写的比较老,使用的一些开源组件已经更新了,有些配置已经不太一样了,所以按照以前的步骤会有问题.折腾了一阵终于跑起来了 ...

  5. Linux是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播。

    Linux是一个基于POSIX和Unix的多用户.多任务.支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播. Linux是众多操作系统之一 , 目前流行的服务器和 PC 端操作系统有 L ...

  6. 【转】发布一个基于NGUI编写的UI框架

    发布一个基于NGUI编写的UI框架 1.加载,显示,隐藏,关闭页面,根据标示获得相应界面实例 2.提供界面显示隐藏动画接口 3.单独界面层级,Collider,背景管理 4.根据存储的导航信息完成界面 ...

  7. 如何使用 Docker 部署一个基于 Play Framework 的 Scala Web 应用?

    本文作者 Jacek Laskowski 拥有近20年的应用程序开发经验,现 CodiLime 的软件开发团队 Leader,曾从 IBM 取得多种资格认证.在这篇博文中,Jacek 分享了 Wars ...

  8. TensorFlow-Bitcoin-Robot:一个基于 TensorFlow LSTM 模型的 Bitcoin 价格预测机器人

    简介 TensorFlow-Bitcoin-Robot:一个基于 TensorFlow LSTM 模型的 Bitcoin 价格预测机器人. 文章包括一下几个部分: 1.为什么要尝试做这个项目? 2.为 ...

  9. Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

随机推荐

  1. 工厂模式讲解, 引入Spring IOC

    目录 引入 简单工厂 抽象工厂 Spring的bean工厂 模拟Spring工厂实现 模拟IOC 引入 假设有一个司机, 需要到某个城市, 于是我们给他一辆汽车 public class Demo { ...

  2. Data Lake Analytics的Geospatial分析函数

    0. 简介 为满足部分客户在云上做Geometry数据的分析需求,阿里云Data Lake Analytics(以下简称:DLA)支持多种格式的地理空间数据处理函数,符合Open Geospatial ...

  3. Springboot 系列(六)Spring Boot web 开发之拦截器和三大组件

    1. 拦截器 Springboot 中的 Interceptor 拦截器也就是 mvc 中的拦截器,只是省去了 xml 配置部分.并没有本质的不同,都是通过实现 HandlerInterceptor ...

  4. DSAPI显示PNG异形窗体

    使用DSAPI实现PNG异形窗体,注意,该窗体为层样式窗体,以PNG或32位带透明通道的图像合成到屏幕,此方法不会触发窗体的重绘,故原窗体(包括其子控件)均不会显示,如果需要更新画面,需要重新用代码等 ...

  5. MySQL 多实例的安装

    Step 1 . 准备工作 关闭防火墙; 安装 MySQL依赖于libaio 库:下载,解压,重命名 MySQL的执行文件:新建用户组和用户 等准备工作在此不再赘述. 可执行文件目录为 /data/m ...

  6. .NET Core 必备安全措施

    .NET Core大大简化了.NET应用程序的开发.它的自动配置和启动依赖大大减少了开始一个应用所需的代码和配置量,本文目的是介绍如何创建更安全的.NET Core应用程序. 1.在生产中使用HTTP ...

  7. JDK对CAS ABA问题解决-AtomicMarkableReference和AtomicStampedReference

    我们知道AtomicInteger和AtomicLong的原子操作,但是在这两个类在CAS操作的时候会遇到ABA问题,可能大家会疑问什么是ABA问题呢,请待我细细道来: ABA问题:简单讲就是多线程环 ...

  8. Python:如何用一行代码获取上个月是几月

    现在转一篇志军100发于公众号 Python之禅的文章: Python:如何用一行代码获取上个月是几月 抱歉我用了个有点标题党的标题,因为担心你错过了本文,但内容绝对干货,本文介绍的关于Python时 ...

  9. Java经典面试题+答案(全)

    这套面试题主要目的是帮助那些还没有java软件开发实际工作经验,而正在努力寻找java软件开发工作的朋友在笔试时更好地赢得笔试和面试. 1.一个".java"源文件中是否可以包括多 ...

  10. PHP全栈学习笔记3

    trim()函数,用于去除字符串首尾空格和特殊字符 返回的是去掉的空格和特殊字符后的字符串 string trim(string str [,string charlist]); str 要操作的字符 ...