Skip to content

Commit

Permalink
Release v0.2.1 (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
132ikl authored Jan 28, 2020
2 parents 5aa7431 + 6b24ce7 commit e1fc93e
Show file tree
Hide file tree
Showing 19 changed files with 583 additions and 37 deletions.
7 changes: 7 additions & 0 deletions opsi/frontend/templates/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ <h2>Import/Export</h2>
<input id="import-button" type="button" value="Import" />
<input id="export-button" type="button" value="Export" />
</div>
<div class="preference">
<h2>Import Camera Calibration</h2>
<form id="import-calibration-form" enctype="multipart/form-data" method="post" name="import-form">
<input type="file" class="bfi" required />
</form>
<input id="import-calibration-button" type="button" value="Import" />
</div>
<div class="preference network-settings">
<h2>Network Config</h2>
<div id="net-normal-settings">
Expand Down
24 changes: 24 additions & 0 deletions opsi/frontend/www/scripts/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,30 @@ $(document).ready(function() {
};
fileReader.readAsText(form[0].files[0]);
});
$("#import-calibration-button").click(function(event) {
event.preventDefault();
var form = $("#import-calibration-form")[0];
var data = new FormData();
data.append("file", form[0].files[0]);
$("#update-button").prop("disabled", true);
setIcons("spinner")
$.ajax({
type: "POST",
enctype: "multipart/form-data",
url: "/api/calibration",
data: data,
processData: false,
contentType: false,
cache: false,
success: function(data) {
setIcons("check")
},
error: function(e) {
console.log(e);
setIcons("cross")
}
});
});
$("#export-button").click(function() {
$("<a />", {
download: "nodetree.json",
Expand Down
7 changes: 1 addition & 6 deletions opsi/manager/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,7 @@ def is_valid_function(cls, module):
def closure(func):
# Todo: are there any other times we don't want to register a Function?
# This is important because the default is registering every single Function
return (
isfunction(func)
and (not func.disabled)
# If a module imports a Function from another module, do not register that Function
and (inspect.getmodule(func) == module or func.force_enabled)
)
return isfunction(func) and not func.disabled

return closure

Expand Down
3 changes: 2 additions & 1 deletion opsi/manager/manager_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ class Function:
require_restart: bool = False
always_restart: bool = False
disabled = False
force_enabled = False

SettingTypes: List[Field]
InputTypes: Dict[str, Type]
Expand Down Expand Up @@ -162,6 +161,8 @@ def _private_validate_settings(cls, settings):

def isfunction(func):
try:
if func is Function:
return False
return issubclass(func, Function)
except TypeError: # func is not a type
return False
Expand Down
21 changes: 21 additions & 0 deletions opsi/modules/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,24 @@ class Outputs:
def run(self, inputs):
img = inputs.img.resize(Point(self.settings.width, self.settings.height))
return self.Outputs(img=img)


class ColorBalance(Function):
@dataclass
class Settings:
red_balance: Slide(min=0, max=100)
blue_balance: Slide(min=0, max=100)

@dataclass
class Inputs:
img: Mat

@dataclass
class Outputs:
img: Mat

def run(self, inputs):
img = inputs.img.color_balance(
self.settings.red_balance / 100.0, self.settings.blue_balance / 100.0
)
return self.Outputs(img=img)
84 changes: 70 additions & 14 deletions opsi/modules/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import numpy as np

from opsi.manager.manager_schema import Function
from opsi.manager.types import Slide
from opsi.util.cv import Contours, Mat, MatBW, Point
from opsi.util.cv.shape import Corners

__package__ = "opsi.contours"
__version__ = "0.123"
Expand Down Expand Up @@ -79,7 +79,7 @@ class Outputs:
def run(self, inputs):
if len(inputs.contours.l) == 0:
return self.Outputs(center=None, success=False, visual=inputs.img)

res = inputs.contours.l[0].res
center = inputs.contours.centroid_of_all

if self.settings.draw:
Expand All @@ -99,7 +99,11 @@ def run(self, inputs):
else:
img = None

return self.Outputs(center=center, success=True, visual=img)
scaled_center = Point(
x=((center.x * 2) / res.x) - 1, y=((center.y * 2) / res.y) - 1
)

return self.Outputs(center=scaled_center, success=True, visual=img)


class FindAngle(Function):
Expand All @@ -118,39 +122,91 @@ def calculate_focal_length(cls, diagonalFOV, horizontalAspect, verticalAspect):
math.atan(math.tan(diagonalView / 2) * (horizontalAspect / diagonalAspect))
* 2
)
# verticalView = math.atan(math.tan(diagonalView/2) * (verticalAspect / diagonalAspect)) * 2
verticalView = (
math.atan(math.tan(diagonalView / 2) * (verticalAspect / diagonalAspect))
* 2
)

# Since point is -1 <= (x, y) <= 1: width, height = 2; center = (0, 0)

# Focal Length calculations: https://docs.google.com/presentation/d/1ediRsI-oR3-kwawFJZ34_ZTlQS2SDBLjZasjzZ-eXbQ/pub?start=false&loop=false&slide=id.g12c083cffa_0_165
H_FOCAL_LENGTH = 2 / (2 * math.tan((horizontalView / 2)))
# V_FOCAL_LENGTH = 2 / (2*math.tan((verticalView/2)))
V_FOCAL_LENGTH = 2 / (2 * math.tan((verticalView / 2)))

return H_FOCAL_LENGTH
return H_FOCAL_LENGTH, V_FOCAL_LENGTH

@dataclass
class Settings:
mode: ("Degrees", "Radians") = "Radians"
diagonalFOV: float = 68.5

@dataclass
class Inputs:
pnt: Point
point: Point
img: Mat

@dataclass
class Outputs:
radians: float
angle: Point

def run(self, inputs):
width = inputs.img.shape[1]
height = inputs.img.shape[0]
width = inputs.img.res.x
height = inputs.img.res.y

center_x = 0
x = inputs.point[0]
x = inputs.point.x
y = inputs.point.y

H_FOCAL_LENGTH = self.calculate_focal_length(
h_focal_length, v_focal_length = self.calculate_focal_length(
self.settings.diagonalFOV, width, height
)
radians = math.atan2(x, H_FOCAL_LENGTH)

return self.Outputs(radians=radians)
if self.settings.mode == "Radians":
radians = Point(
x=math.atan2(x, h_focal_length), y=math.atan2(y, v_focal_length)
)
return self.Outputs(angle=radians)
else:
degrees = Point(
x=math.degrees(math.atan2(x, h_focal_length)),
y=math.degrees(math.atan2(y, v_focal_length)),
)
return self.Outputs(angle=degrees)


class FindCorners(Function):
@dataclass
class Inputs:
contours: Contours

@dataclass
class Outputs:
corners: Corners
success: bool

def run(self, inputs):
if len(inputs.contours.l) == 0:
return self.Outputs(corners=None, success=False)

cnt = inputs.contours.l[0]

ret, corners = cnt.corners

return self.Outputs(corners=corners, success=ret)


class FindArea(Function):
@dataclass
class Inputs:
contours: Contours

@dataclass
class Outputs:
area: float

def run(self, inputs):
if inputs.contours is None or len(inputs.contours.l) == 0:
return self.Outputs(area=0)
else:
return self.Outputs(area=sum([cnt.area for cnt in inputs.contours.l]))

2 changes: 1 addition & 1 deletion opsi/modules/draw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from opsi.util.cv import Contours, Mat, MatBW

from .fps import DrawFPS
from .shapes import DrawCircles, DrawSegments
from .shapes import DrawCircles, DrawCorners, DrawSegments

__package__ = "opsi.draw"
__version__ = "0.123"
Expand Down
2 changes: 0 additions & 2 deletions opsi/modules/draw/fps.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ def fps(self):


class DrawFPS(Function):
force_enabled = True

def on_start(self):
self.f = FPS()
self.f.start()
Expand Down
33 changes: 28 additions & 5 deletions opsi/modules/draw/shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@

from opsi.manager.manager_schema import Function
from opsi.util.cv import Mat
from opsi.util.cv.shape import Circles, Segments
from opsi.util.cv.shape import Circles, Corners, Segments


class DrawCircles(Function):
force_enabled = True

@dataclass
class Inputs:
circles: Circles
Expand Down Expand Up @@ -43,8 +41,6 @@ def run(self, inputs):


class DrawSegments(Function):
force_enabled = True

@dataclass
class Inputs:
lines: Segments
Expand All @@ -67,3 +63,30 @@ def run(self, inputs):

draw = Mat(draw)
return self.Outputs(img=draw)


class DrawCorners(Function):
force_enabled = True

@dataclass
class Inputs:
corners: Corners
img: Mat

@dataclass
class Outputs:
img: Mat

def run(self, inputs):
# If there are no circles return the input image
if inputs.corners is None:
return self.Outputs(img=inputs.img)
img = np.copy(inputs.img.mat.img)

for corner in inputs.corners:
cv2.circle(
img, (int(corner.x), int(corner.y)), 5, (0, 0, 255), 3,
)
img = Mat(img)

return self.Outputs(img=img)
1 change: 0 additions & 1 deletion opsi/modules/gpio.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from dataclasses import dataclass

import gpiozero

from opsi.manager.manager_schema import Function

__package__ = "opsi.gpio"
Expand Down
Loading

0 comments on commit e1fc93e

Please sign in to comment.