一个基于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 ...
随机推荐
- [翻译 EF Core in Action 2.0] 查询数据库
Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...
- Linux V4L2之camera
一.硬件知识 1. 摄像头硬件结构和工作原理,如图1&图2 外部光线穿过lens镜头,经过红外滤光片后光学图像投射到传感器上,然后光学图像被转换成电信号,电信号再经过模数转换变为数字信号,数字 ...
- 8天入门docker系列 —— 第二天 通过一个aspnetcore程序加深对容器的理解
我们知道容器是一个打包了应用和相关依赖的盒子,那怎么去操控这个盒子呢? 这一篇我通过一个简单的aspnetcore程序来加深对盒子的理解,使用之前先 安装一下Docker的环境. 一:Docker的安 ...
- HTML文档编写规范
(1)HTML标记是由尖括号包围的关键词.所有标记均以“<”开始,以“>”结束.结束的标记在开始名称前加上斜杠“/”.例如头部标记格式如下所示:<head> ……</he ...
- Odoo 12 开发手册指南(八)—— 业务逻辑 – 业务流程的支持
在前面的文章中,我们学习了模型层.如何创建应用数据结构以及如何使用 ORM API 来存储查看数据.本文中我们将利用前面所学的模型和记录集知识实现应用中常用的业务逻辑模式. 本文的主要内容有: 以文件 ...
- 写个重新加载 ocelot 配置的接口
写个重新加载 ocelot 配置的接口 Intro 我们想把 ocelot 的配置放在自己的存储中,放在 Redis 或者数据库中,当修改了 Ocelot 的配置之后希望即时生效,又不想在网关这边定时 ...
- Sqlserver 事务处理模板
USE StuDB GO /****** Object: StoredProcedure [dbo].[proc_live_send_answer_v4] Script Date: 06/20/201 ...
- vue项目使用 prerender-spa-plugin 预渲染
由于项目要做seo优化,而用vue写成的spa页面谷歌浏览器等是抓取不到数据的.介于ssr和预渲染来说,后者相对来说要简单许多.所以采用了预渲染方式.采用插件prerender-spa-plugin使 ...
- web server性能优化浅谈
作者:ZhiYan,Jack47 转载请保留作者和原文出处 Update: 2018.8.8 在无锁小节增加了一些内容 性能优化,优化的东西一定得在主路径上,结合测量的结果去优化.不然即使性能再好,逻 ...
- 使用 docker-compose 快速安装Jenkins
本文分享在 docker 环境中,使用 docker-compose.yml 快速安装 Jenkins,以及使用主机中的 docker 打包推送镜像到阿里云 博客园的第100篇文章达成,2019的第一 ...