Commit b93989c5 authored by Darko's avatar Darko

Add multiple points

parent 5b6ecaf9
from flask import Flask, request, send_file, make_response, jsonify, session, send_from_directory
from flask_cors import CORS
from flask_cors import CORS, cross_origin
import os
import cv2
import numpy as np
......@@ -12,7 +12,7 @@ app = Flask(__name__, static_url_path='')
app.config['SESSION_TYPE'] = 'filesystem'
session_ext = Session()
session_ext.init_app(app)
CORS(app, supports_credentials=True)
CORS(app, supports_credentials=True, resources={r"/*": {"origins": "*"}})
......@@ -47,57 +47,71 @@ def clear():
return jsonify(session['corners'])
@app.route('/next', methods=['GET'])
def next():
n = int(request.args.get('n', default=1))
for _ in range(n):
last = session['corners'][-1]
blast = session['corners'][-2]
filename = session['img']
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
next_c = [
2 * last[0] - blast[0],
2 * last[1] - blast[1],
]
next_c_t = np.array([[ next_c ]]).astype(np.float32)
corners = cv2.cornerSubPix(
gray,
next_c_t,
(15, 15),
(-1, -1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
)
session['corners'].append([
float(corners[0, 0, 0]),
float(corners[0, 0, 1]),
])
return jsonify(session['corners'])
@app.route('/add', methods=['GET'])
def add():
x = int(request.args.get('x'))
y = int(request.args.get('y'))
w = int(request.args.get('w'))
h = int(request.args.get('h'))
n = int(request.args.get('n', default=1))
tol = float(request.args.get('tol', default=0.3))
# Load image
filename = session['img']
img = cv2.imread(filename)
# Find corners in subimage
startX = max([0, x-w//2])
endX = min([img.shape[1], x+w//2])
startY = max([0, y-h//2])
endY = min([img.shape[0], y+h//2])
subimg = img[startY:endY, startX:endX]
subgray = cv2.cvtColor(subimg, cv2.COLOR_RGB2GRAY)
corners = cv2.goodFeaturesToTrack(subgray, n, tol, 10, useHarrisDetector=True)
# If not corners found use selected one
if corners is None:
corners = np.array([ [ [ h/2, w/2 ] ] ], dtype=np.float32)
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# Convert to absolute
abs_corners = []
subcorners = cv2.cornerSubPix(
subgray,
corners,
(5, 5),
(-1, -1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
)
for corner in subcorners.tolist():
cx = corner[0][0] + x - w/2
cy = corner[0][1] + y - h/2
abs_corners.append([ cx, cy ])
corners = np.array([[ [x, y] ]]).astype(np.float32)
if w > 5:
corners = cv2.cornerSubPix(
gray,
corners,
(w, w),
(-1, -1),
(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
)
# Put corners to session
for corner in abs_corners:
overlap = False
for corner_prev in session['corners']:
if abs(corner_prev[0] - corner[0]) < 2 and abs(corner_prev[1] - corner[1]) < 2:
overlap = True
if not overlap:
session['corners'].append(corner)
session.modified = True
overlap = False
for corner_prev in session['corners']:
if abs(corner_prev[0] - x) < 2 and abs(corner_prev[1] - y) < 2:
overlap = True
if not overlap:
session['corners'].append([
float(corners[0, 0, 0]),
float(corners[0, 0, 1]),
])
# Return corners
return jsonify(session['corners'])
......
......@@ -4,6 +4,8 @@
<head>
<meta charset="utf-8">
<title>Calibration Tool | Calibration UI for Highly Undistorted Images</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' />
<meta name="description" content="Select chessboard area with brush and Harris detector will automatically find corners. Additional precision is obtained using subpixel corner detection. If detection fails you can use a rubber to delete points and brush to draw them again. The points can be exported as CSV file.">
</head>
<body>
......
......@@ -2312,6 +2312,11 @@
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true
},
"custom-event-polyfill": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz",
"integrity": "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w=="
},
"cyclist": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
......@@ -2567,6 +2572,11 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.136.tgz",
"integrity": "sha512-xHkYkbEi4kI+2w5v6yBGCQTRXL7N0PWscygTFZu/1bArnPSo2WR9xjdw4m06RR4J5PncrWJcuOVv+MAG2mK5JQ=="
},
"element-matches": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/element-matches/-/element-matches-0.1.2.tgz",
"integrity": "sha512-yWh1otcs3OKUWDvu/IxyI36ZI3WNaRZlI0uG/DK6fu0pap0VYZ0J5pEGTk1zakme+hT0OKHwhlHc0N5TJhY6yQ=="
},
"elliptic": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
......@@ -6837,6 +6847,15 @@
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.6.tgz",
"integrity": "sha512-Ox0ciFLswtSGRTHYhGvx2L44sVbTPNS+uD2kRISuo8B39Y79rOo0Kw0hzupTmiVtftQYCZl87mwldhh2L9Aquw=="
},
"vue-shortkey": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/vue-shortkey/-/vue-shortkey-3.1.7.tgz",
"integrity": "sha512-Wm/vPXXS+4Wl/LoYpD+cZc0J0HIoVlY8Ep0JLIqqswmAya3XUBtsqKbhzEf9sXo+3rZ5p1YsUyZfXas8XD7YjQ==",
"requires": {
"custom-event-polyfill": "^1.0.7",
"element-matches": "^0.1.2"
}
},
"vue-slider-component": {
"version": "3.0.31",
"resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-3.0.31.tgz",
......
......@@ -6,10 +6,10 @@
"license": "MIT",
"private": true,
"scripts": {
"dev": "webpack-dev-server --mode=development --open --hot",
"dev": "webpack-dev-server --public=0.0.0.0:8080 --mode=development --open --hot",
"build": "webpack --mode=production --progress --hide-modules",
"heroku-prebuild": "npm install",
"heroku-postbuild": "webpack --mode=production --progress --hide-modules"
"heroku-postbuild": "npm run build"
},
"dependencies": {
"@babel/core": "^7.4.5",
......@@ -22,6 +22,7 @@
"popper.js": "^1.15.0",
"vue": "^2.5.11",
"vue-router": "^3.0.6",
"vue-shortkey": "^3.1.7",
"vue-slider-component": "^3.0.31",
"vue2-dropzone": "^3.5.9"
},
......
......@@ -2,7 +2,7 @@ import axios from "axios";
const api = axios.create({
baseURL: API_URL,
timeout: 1000,
timeout: 3000,
withCredentials: true,
});
......
......@@ -8,10 +8,14 @@ import VueSlider from 'vue-slider-component'
import 'vue-slider-component/theme/default.css'
import Bootstrap from 'bootstrap/dist/css/bootstrap.css';
import './assets/style.css'
import ShortKey from 'vue-shortkey';
Vue.component('VueSlider', VueSlider)
Vue.use(VueRouter)
Vue.use(ShortKey)
const NotFound = { template: '<div>Not found</div>' }
......
......@@ -9,7 +9,7 @@
<hr>
# points: <b>{{ nPoints }}</b>
# points: <b>{{ nPoints }}</b> / {{ nPoints % 19 }}
<hr />
......@@ -22,29 +22,28 @@
</label>
</div>
<hr>
<hr />
<button class="btn btn-secondary" v-shortkey="['n']" @shortkey="nextClicked(1)" v-on:click="nextClicked(1)">Next</button>
<button class="btn btn-secondary" v-shortkey="['m']" @shortkey="nextClicked(5)" v-on:click="nextClicked(5)">Next x5</button>
<hr />
<vue-slider
id="brush-size"
v-model="brushSize"
:min="16"
:interval="2"
:max="1000"
:min="1"
:interval="1"
:max="50"
:tooltip-formatter="brushSizeFormatter"
/>
<vue-slider
v-model="brushMaxCorners"
:min="0"
:max="100"
:tooltip-formatter="brushMaxCornersFormatter"
/>
<vue-slider
v-model="brushTolerance"
:interval="0.01"
:min="0"
:max="1"
:tooltip-formatter="brushToleranceFormatter"
:tooltip-formatter="brushMaxCornersFormatter"
/>
<hr>
......@@ -74,6 +73,7 @@ import Stroke from "ol/style/Stroke.js";
import Text from "ol/style/Text.js";
import LineString from "ol/geom/LineString.js";
import api from "../common.js";
import WebGLMap from 'ol/WebGLMap';
export default {
mounted() {
......@@ -102,6 +102,13 @@ export default {
api.get("/list").then(this.showPoints);
},
nextClicked(n=1) {
console.log(n);
api.get("/next", {
params: { n: n }
}).then(this.showPoints);
},
removePoints(x, y) {
api
.get("/remove", {
......@@ -190,11 +197,14 @@ export default {
source: new Static({
url: this.img.src,
projection: "EPSG:3857",
imageExtent: extent
imageExtent: extent,
// attributions: '© <a href="https://lukic.io">Darko Lukic</a>',
// crossOrigin: 'anonymous',
})
})
}),
],
view: new View({
enableRotation: false,
projection: projection,
center: getCenter(extent),
resolution: 4,
......@@ -220,7 +230,7 @@ export default {
tool: "brush",
brushSize: 16,
brushMaxCorners: 30,
brushMaxCorners: 1,
brushTolerance: 0.5,
brushSizeFormatter: "Brush size {value}px",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment