一个基于OCV的人肉选取特征点程序
基于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的人肉选取特征点程序的更多相关文章
- Xamarin 小试牛刀 通知栏消息通知和按钮(基于Java代码人肉转换)
本示例基于网友现有安卓项目人肉翻译,在Xamarin中替换和修改了很多方法的命名,比如某些属性需要去掉getName的get前缀, 有些方法名称需要使用Pascal命名法替换Java的Camel 命名 ...
- 经验分享:一个 30 岁的人是如何转行做程序员,进入IT行业的?
大约一年以前,我成为了一名全职开发者,我想要总结一下这一年的经验,并且和所有人分享,一个 30 多岁的人是如何进入科技行业的: 改变职业是一件吓人的事情,有时候还会成为一件危险的事情.年龄越大,危险就 ...
- eShopOnContainers 是一个基于微服务的.NET Core示例框架
找到一个好的示例框架很难,但不是不可能.大多数是小型Todo风格的应用程序,通常基于SimpleCRUD.值得庆幸的是,Microsoft已经为eShopOnContainers创建了一个基于微服务的 ...
- 在Ubuntu上部署一个基于webrtc的多人视频聊天服务
最近研究webrtc视频直播技术,网上找了些教程最终都不太能顺利跑起来的,可能是文章写的比较老,使用的一些开源组件已经更新了,有些配置已经不太一样了,所以按照以前的步骤会有问题.折腾了一阵终于跑起来了 ...
- Linux是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播。
Linux是一个基于POSIX和Unix的多用户.多任务.支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播. Linux是众多操作系统之一 , 目前流行的服务器和 PC 端操作系统有 L ...
- 【转】发布一个基于NGUI编写的UI框架
发布一个基于NGUI编写的UI框架 1.加载,显示,隐藏,关闭页面,根据标示获得相应界面实例 2.提供界面显示隐藏动画接口 3.单独界面层级,Collider,背景管理 4.根据存储的导航信息完成界面 ...
- 如何使用 Docker 部署一个基于 Play Framework 的 Scala Web 应用?
本文作者 Jacek Laskowski 拥有近20年的应用程序开发经验,现 CodiLime 的软件开发团队 Leader,曾从 IBM 取得多种资格认证.在这篇博文中,Jacek 分享了 Wars ...
- TensorFlow-Bitcoin-Robot:一个基于 TensorFlow LSTM 模型的 Bitcoin 价格预测机器人
简介 TensorFlow-Bitcoin-Robot:一个基于 TensorFlow LSTM 模型的 Bitcoin 价格预测机器人. 文章包括一下几个部分: 1.为什么要尝试做这个项目? 2.为 ...
- 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 ...
随机推荐
- Java8新特性之三:Stream API
Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式.Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找.过滤.筛选等操作 ...
- Spring+SpringMVC+MyBatis的pom.xml依赖
<!-- 集中定义依赖版本号 --> <!-- 已经依据maven仓库给出的版本兼容信息,调节好合适的spring.mybatis-spring.mybatis.pagehelper ...
- netcore使用 jenkins + supervisor 实现standalone下多副本自动化发布
上一篇我们用jenkins做了一个简单的自动化发布,在shell中采用的是 BUILD_ID=dontKillMe nohup dotnet xxx.dll & 这种简单的后台承载,如果你的 ...
- 设计模式 | 观察者模式/发布-订阅模式(observer/publish-subscribe)
定义: 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 结构:(书中图,侵删) 一个抽象的观察者接口, ...
- 解决Android编译时出现aapt.exe finished with non-zero exit value 1
当出现这个错误的时候,说明了你的资源文件出错了.然而AS能给你提供的信息实在太少,看了半天没看出个所以然,也没有说明是哪个资源文件出错,一头雾水. 这时候就可以用 Gradlew 来调试. 而grad ...
- python查询修改配置文件功能
阅读目录 一.python查询功能代码 1.查询修改配置文件 global log 127.0.0.1 local2 daemon maxconn 256 log 127.0.0.1 local2 i ...
- 阿里云或本地部署服务器(一)---nginx本地和服务器代理
具体步骤: 1.nginx下载 2.在G:\nginx-1.15.8\conf/nginx.conf改三处路径:nginx.conf 文件中配置的路径和端口要映射到vue项目工程 a.改 映射端口: ...
- Spring IOC源码实现流程
最近一段时间学习了IOC的源码实现,分享一下大概流程 创建一个测试类 代码如下: package com.zcg.learn.Test; import org.junit.Test;import or ...
- @Autowired注解警告Field injection is not recommended
在使用spring框架中的依赖注入注解@Autowired时,idea报了一个警告 大部分被警告的代码都是不严谨的地方,所以我深入了解了一下. 被警告的代码如下: @Autowired UserDao ...
- Packets larger than max_allowed_packet are not allowed(mysql数据查询提示:不允许超过允许的最大数据包)解决方案
问题背景: 首先我的项目运用的时Mysql,在做一个数据更新操作的时候,提示异常:System.Exception:“ExecuteNonQuery:Packets larger than max_a ...