-
-
Save JT5D/e9f27ae67691611e2eaff75762664607 to your computer and use it in GitHub Desktop.
Circle detection with OpenCV 3.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cv2 | |
import time | |
import math | |
import numpy as np | |
capture = cv2.VideoCapture(0) | |
print capture.get(cv2.CAP_PROP_FPS) | |
t = 100 | |
w = 640.0 | |
last = 0 | |
while True: | |
ret, image = capture.read() | |
img_height, img_width, depth = image.shape | |
scale = w / img_width | |
h = img_height * scale | |
image = cv2.resize(image, (0,0), fx=scale, fy=scale) | |
# Apply filters | |
grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
blured = cv2.medianBlur(grey, 15) | |
# Compose 2x2 grid with all previews | |
grid = np.zeros([2*h, 2*w, 3], np.uint8) | |
grid[0:h, 0:w] = image | |
# We need to convert each of them to RGB from grescaled 8 bit format | |
grid[h:2*h, 0:w] = np.dstack([cv2.Canny(grey, t / 2, t)] * 3) | |
grid[0:h, w:2*w] = np.dstack([blured] * 3) | |
grid[h:2*h, w:2*w] = np.dstack([cv2.Canny(blured, t / 2, t)] * 3) | |
cv2.imshow('Image previews', grid) | |
sc = 1 | |
md = 30 | |
at = 40 | |
circles = cv2.HoughCircles(blured, cv2.HOUGH_GRADIENT, sc, md, t, at) | |
if circles is not None: | |
# We care only about the first circle found. | |
circle = circles[0][0] | |
x, y, radius = int(circle[0]), int(circle[1]), int(circle[2]) | |
print(x, y, radius) | |
# Highlight the circle | |
cv2.circle(image, (x, y), radius, (0, 0, 255), 1) | |
# Draw dot in the center | |
cv2.circle(image, (x, y), 1, (0, 0, 255), 1) | |
cv2.imshow('Image with detected circle', image) | |
if cv2.waitKey(1) & 0xFF == ord('q'): | |
break |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cv2 | |
import numpy as np | |
import threading | |
import json | |
from SimpleWebSocketServer import SimpleWebSocketServer, WebSocket | |
server = None | |
clients = [] | |
class SimpleWSServer(WebSocket): | |
def handleConnected(self): | |
clients.append(self) | |
def handleClose(self): | |
clients.remove(self) | |
def run_server(): | |
global server | |
server = SimpleWebSocketServer('', 9000, SimpleWSServer, | |
selectInterval=(1000.0 / 15) / 1000) | |
server.serveforever() | |
t = threading.Thread(target=run_server) | |
t.start() | |
capture = cv2.VideoCapture(0) | |
t = 100 | |
w = 640.0 | |
last = 0 | |
while True: | |
ret, image = capture.read() | |
img_height, img_width, depth = image.shape | |
scale = w / img_width | |
h = img_height * scale | |
image = cv2.resize(image, (0,0), fx=scale, fy=scale) | |
# Apply filters | |
grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
blured = cv2.medianBlur(grey, 9) | |
# Compose 2x2 grid with all previews | |
grid = np.zeros([2*h, 2*w, 3], np.uint8) | |
grid[0:h, 0:w] = image | |
# We need to convert each of them to RGB from grescaled 8 bit format | |
grid[h:2*h, 0:w] = np.dstack([cv2.Canny(grey, t / 2, t)] * 3) | |
grid[0:h, w:2*w] = np.dstack([blured] * 3) | |
grid[h:2*h, w:2*w] = np.dstack([cv2.Canny(blured, t / 2, t)] * 3) | |
cv2.imshow('Image previews', grid) | |
sc = 1 | |
md = 30 | |
at = 40 | |
circles = cv2.HoughCircles(blured, cv2.HOUGH_GRADIENT, sc, md, t, at) | |
if circles is not None: | |
# We care only about the first circle found. | |
circle = circles[0][0] | |
x, y, radius = int(circle[0]), int(circle[1]), int(circle[2]) | |
print(x / w, y / h, radius / w) | |
# Highlight the circle | |
cv2.circle(image, (x, y), radius, (0, 0, 255), 1) | |
# Draw dot in the center | |
cv2.circle(image, (x, y), 1, (0, 0, 255), 1) | |
for client in clients: | |
client.sendMessage(unicode(json.dumps({'x': x / w, 'y': y / h, 'radius': radius / w}))) | |
cv2.imshow('Image with detected circle', image) | |
if cv2.waitKey(1) & 0xFF == ord('q'): | |
break | |
server.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
<style> | |
body { margin: 0; } | |
canvas { width: 100%; position: absolute; top: 0; } | |
video { width: 100%; } | |
</style> | |
<video autoplay></video> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/2.4.0/math.min.js"></script> | |
<script src="lib/three.72.min.js"></script> | |
<script src="main.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var scene, camera, renderer, light; | |
var earthRotY = 0, moonRotY = 0; | |
var radY = 0, radZ = -0.3; | |
var moonDist = 70; | |
var earthRadius = 25; | |
var earthMesh, tmpMesh; | |
var moonMesh; | |
var positionHistory = []; | |
var lastPos, diffMove, lastEarthScale; | |
var ping = 0; | |
function init(width, height) { | |
scene = new THREE.Scene(); | |
// Setup cameta with 45 deg field of view and same aspect ratio | |
var aspect = width / height; | |
camera = new THREE.PerspectiveCamera(45, aspect, 0.1, 1000); | |
// Set the camera to 400 units along `z` axis | |
camera.position.set(0, 0, 400); | |
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); | |
renderer.setSize(width, height); | |
renderer.shadowMap.enabled = true; | |
document.body.appendChild(renderer.domElement); | |
} | |
function initLight() { | |
light = new THREE.SpotLight(0xffffff); | |
// Position the light slightly to a side to make | |
// shadows look better. | |
light.position.set(400, 100, 1000); | |
light.castShadow = true; | |
scene.add(light); | |
} | |
function initEarth() { | |
// Load Earth texture and create material from it | |
var earthTexture = THREE.ImageUtils.loadTexture("/images/earthmap1k.jpg"); | |
earthTexture.minFilter = THREE.NearestFilter; | |
var earthMaterial = new THREE.MeshLambertMaterial({ | |
map: earthTexture, | |
}); | |
// Create a sphere 25 units in radius and 16 segments | |
// both horizontally and vertically. | |
var earthGeometry = new THREE.SphereGeometry(earthRadius, 16, 16); | |
earthMesh = new THREE.Mesh(earthGeometry, earthMaterial); | |
earthMesh.receiveShadow = true; | |
earthMesh.castShadow = true; | |
// Add Earth to the scene | |
scene.add(earthMesh); | |
} | |
function initMoon() { | |
var moonTexture = THREE.ImageUtils.loadTexture("/images/moonmap1k-lowres.jpg"); | |
moonTexture.minFilter = THREE.NearestFilter; | |
var moonMaterial = new THREE.MeshLambertMaterial({ | |
map: moonTexture, | |
}); | |
var moonGeometry = new THREE.SphereGeometry(earthRadius * 0.273, 10, 10); | |
moonMesh = new THREE.Mesh(moonGeometry, moonMaterial); | |
moonMesh.receiveShadow = true; | |
moonMesh.castShadow = true; | |
scene.add(moonMesh); | |
} | |
function initPlane() { | |
// The plane needs to be large to be sure it'll always intersect | |
var tmpGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1); | |
tmpGeometry.position = new THREE.Vector3(0, 0, 0); | |
tmpMesh = new THREE.Mesh(tmpGeometry); | |
} | |
// Update position of objects in the scene | |
function update() { | |
if (positionHistory.length === 0) { | |
return; | |
} | |
earthRotY += 0.007; | |
ping++; | |
if (ping < 10) { | |
lastPos[0] += diffMove[0]; | |
lastPos[1] += diffMove[1]; | |
lastPos[2] += diffMove[2]; | |
} | |
var vector = new THREE.Vector3(lastPos[0], lastPos[1], 0.5); | |
var intersect = checkIntersect(vector); | |
earthMesh.rotation.y = earthRotY; | |
// With position from OpenCV I could possibly move the Earth outside of the window | |
if (intersects.length === 1) { | |
var point = intersects[0].point; | |
earthMesh.position.x = point.x; | |
earthMesh.position.y = point.y; | |
// X pos + radius | |
var vector = new THREE.Vector3(lastPos[0] + lastPos[2], lastPos[1], 0.5); | |
var intersect = checkIntersect(vector); | |
var newEarthRadius = Math.abs(intersect.x - earthMesh.position.x); | |
var earthScale = newEarthRadius / earthRadius; | |
earthMesh.scale.set(earthScale, earthScale, earthScale); | |
moonMesh.scale.set(earthScale, earthScale, earthScale); | |
lastEarthScale = earthScale; | |
} | |
moonRotY += 0.005; | |
radY += 0.03; | |
radZ += 0.0005; | |
// Update Moon position | |
x = lastEarthScale * moonDist * Math.cos(radZ) * Math.sin(radY); | |
y = lastEarthScale * moonDist * Math.sin(radZ) * Math.sin(radY); | |
z = lastEarthScale * moonDist * Math.cos(radY); | |
moonMesh.position.set(x + earthMesh.position.x, y + earthMesh.position.y, z); | |
moonMesh.rotation.y = moonRotY; | |
} | |
function checkIntersect(vector) { | |
// Unproject camera distortion (fov, aspect ratio) | |
vector.unproject(camera); | |
var norm = vector.sub(camera.position).normalize(); | |
var ray = new THREE.Raycaster(camera.position, norm); | |
// Cast a line from our camera to the tmpMesh and see where these | |
// two intersect. That's our 2D position in 3D coordinates. | |
var intersects = ray.intersectObject(tmpMesh); | |
return intersects[0].point; | |
} | |
// Redraw entire scene | |
function render() { | |
update(); | |
renderer.setClearColor(0x000000, 0); | |
renderer.render(scene, camera); | |
// Schedule another frame | |
requestAnimationFrame(render); | |
} | |
//function onDocumentMouseMove(event) { | |
// // Current mouse position with [0,0] in the center of the document | |
// // and ranging from -1.0 to +1.0 with `y` axis inverted. | |
// mouse.x = (event.clientX / window.innerWidth) * 2 - 1; | |
// mouse.y = - (event.clientY / window.innerHeight) * 2 + 1; | |
//} | |
document.addEventListener('DOMContentLoaded', function(event) { | |
// Initialize everything and start rendering | |
navigator.getUserMedia = navigator.getUserMedia || | |
navigator.webkitGetUserMedia || | |
navigator.mozGetUserMedia || | |
navigator.msGetUserMedia; | |
var video = document.querySelector('video'); | |
var constrains = { | |
video: { | |
mandatory: { | |
minWidth: window.innerWidth, | |
} | |
} | |
}; | |
if (navigator.getUserMedia) { | |
navigator.getUserMedia(constrains, function(stream) { | |
video.src = window.URL.createObjectURL(stream); | |
video.oncanplay = function() { | |
init(video.clientWidth, video.clientHeight); | |
initEarth(); | |
initMoon(); | |
initLight(); | |
initPlane(); | |
requestAnimationFrame(render); | |
} | |
}, function() {}); | |
} | |
}); | |
var ws = new WebSocket('ws://localhost:9000'); | |
ws.onopen = function() { | |
console.log('onopen'); | |
}; | |
ws.onmessage = function (event) { | |
var msg = JSON.parse(event.data); | |
positionHistory.push({ | |
x: msg.x * 2 - 1, | |
y: - msg.y * 2 + 1, | |
radius: msg.radius | |
}); | |
if (positionHistory.length > 10) { | |
positionHistory.shift(); | |
} | |
var xCoords = [], yCoords = [], radiuses = []; | |
for (var i = math.max(positionHistory.length - 2, 0); i < positionHistory.length; i++) { | |
xCoords.push(positionHistory[i].x); | |
yCoords.push(positionHistory[i].y); | |
} | |
for (var i = 0; i < positionHistory.length; i++) { | |
radiuses.push(positionHistory[i].radius); | |
} | |
var posX = math.mean(xCoords); | |
var posY = math.mean(yCoords); | |
var radius = math.mean(radiuses); | |
var targetPos = [posX, posY, radius]; | |
if (!lastPos) { | |
lastPos = targetPos; | |
} | |
diffMove = [(targetPos[0] - lastPos[0]) / 4, (targetPos[1] - lastPos[1]) / 4, (targetPos[2] - lastPos[2]) / 4] | |
ping = 0; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment