基于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 Boot入门(三):使用Scheduled注解实现定时任务

    在程序开发的过程中,经常会使用定时任务来实现一些功能,比如: 系统依赖于外部系统的非核心数据,可以定时同步 系统内部一些非核心数据的统计计算,可以定时计算 系统内部的一些接口,需要间隔几分钟或者几秒执 ...

  2. 使用JDBC操作MySQL数据库

    一.JDBC简介 JDBC(Java DataBase Connectivity)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一的访问,它由一组用Java语言编写的类和接口组 ...

  3. MySQL数据库Inception工具学习与测试 笔记

    MySQL语句的审核,在业界都已经基本被认同了,实际上也是对MySQL语句写法的统一化,标准化,而之前的人工审核,针对标准这个问题其实是很吃力的,标准越多,DBA越累,开发也越累. 那么在这个都追求自 ...

  4. 【原】无脑操作:TypeScript环境搭建

    概述:本文描述TypeScript环境搭建,以及基于VSCode的自动编译设置和调试设置.网络上很多相应文章的方式过时了或者无法试验成功. ------------------------------ ...

  5. Spring IOC源码实现流程

    最近一段时间学习了IOC的源码实现,分享一下大概流程 创建一个测试类 代码如下: package com.zcg.learn.Test; import org.junit.Test;import or ...

  6. vue 中 vue-router、transition、keep-alive 怎么结合使用?

    <transition :name="name" mode="out-in" name="fade"> <keep-ali ...

  7. npm 使用 taobao 的镜像后,无法 login & publish

    使用 npm adduser,添加用户之后,没有异常消息,然后使用 npm publish 发布,却报错: 401 原来是 npm 使用 taobao 的镜像后,需要指定 --registry htt ...

  8. Linux 桌面玩家指南:09. X Window 的奥秘

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...

  9. 使用 docker-compose 快速安装Jenkins

    本文分享在 docker 环境中,使用 docker-compose.yml 快速安装 Jenkins,以及使用主机中的 docker 打包推送镜像到阿里云 博客园的第100篇文章达成,2019的第一 ...

  10. 难以理解的AQS(下)

    在上一篇博客,简单的说下了AQS的基本概念,核心源码解析,但是还有一部分内容没有涉及到,就是AQS对条件变量的支持,这篇博客将着重介绍这方面的内容. 条件变量 基本应用 我们先通过模拟一个消费者/生产 ...