Updated all log strings to new format
This commit is contained in:
parent
36e837d374
commit
9f5252194a
35 changed files with 121 additions and 715 deletions
|
|
@ -1,101 +0,0 @@
|
|||
from io import BytesIO
|
||||
|
||||
import numpy as np
|
||||
import requests
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class APIconnection:
|
||||
def __init__(self, host="localhost", port=5000, api_ver="v1"):
|
||||
self.base = self.build_base(host=host, port=port, api_ver=api_ver)
|
||||
|
||||
def build_base(self, host, port, api_ver):
|
||||
return "http://{}:{}/api/{}".format(host, port, api_ver)
|
||||
|
||||
def uri(self, suffix):
|
||||
return self.base + suffix
|
||||
|
||||
def get(self, route, json=True, timeout=5):
|
||||
r = requests.get(self.uri(route), timeout=timeout)
|
||||
if json:
|
||||
return r.json()
|
||||
else:
|
||||
return r
|
||||
|
||||
def post(self, route, json=None, timeout=5):
|
||||
r = requests.post(self.uri(route), json=json, timeout=timeout)
|
||||
return r.json()
|
||||
|
||||
def delete(self, route, timeout=5):
|
||||
r = requests.delete(self.uri(route), timeout=timeout)
|
||||
return r.json()
|
||||
|
||||
def set_overlay(self, message="", size=50):
|
||||
json = {"text": message, "size": size}
|
||||
return self.post("/camera/overlay", json=json)
|
||||
|
||||
def get_overlay(self):
|
||||
return self.get("/camera/overlay")
|
||||
|
||||
def get_config(self):
|
||||
return self.get("/config")
|
||||
|
||||
def set_config(self, config_dict):
|
||||
return self.post("/config", json=config_dict)
|
||||
|
||||
def get_state(self):
|
||||
return self.get("/state")
|
||||
|
||||
def start_preview(self):
|
||||
return self.post("/camera/preview/start")
|
||||
|
||||
def stop_preview(self):
|
||||
return self.post("/camera/preview/stop")
|
||||
|
||||
def move_by(self, x=0, y=0, z=0):
|
||||
json = {"x": x, "y": y, "z": z}
|
||||
return self.post("/stage/position", json=json)
|
||||
|
||||
def new_capture(self, use_video_port=True, keep_on_disk=False, resize=None):
|
||||
json = {"keep_on_disk": keep_on_disk, "use_video_port": use_video_port}
|
||||
|
||||
if resize:
|
||||
json["size"] = {"width": resize[0], "height": resize[1]}
|
||||
|
||||
return self.post("/camera/capture", json=json)
|
||||
|
||||
def get_capture(self, capture_id):
|
||||
uri_route = "/camera/capture/{}/download".format(capture_id)
|
||||
r = self.get(uri_route, json=False)
|
||||
img = Image.open(BytesIO(r.content))
|
||||
array = np.asarray(img, dtype=np.int32)
|
||||
return array
|
||||
|
||||
def del_capture(self, capture_id):
|
||||
uri_route = "/camera/capture/{}".format(capture_id)
|
||||
return self.delete(uri_route)
|
||||
|
||||
def capture(
|
||||
self,
|
||||
use_video_port=True,
|
||||
keep_on_disk=False,
|
||||
delete_after_use=True,
|
||||
resize=None,
|
||||
):
|
||||
p = self.new_capture(
|
||||
use_video_port=use_video_port, keep_on_disk=keep_on_disk, resize=resize
|
||||
)
|
||||
capture_id = p["metadata"]["id"]
|
||||
img_array = self.get_capture(capture_id)
|
||||
|
||||
if delete_after_use:
|
||||
self.del_capture(capture_id)
|
||||
|
||||
return img_array
|
||||
|
||||
def set_zoom(self, zoom_value=1.0):
|
||||
json = {"zoom_value": zoom_value}
|
||||
return self.post("/camera/zoom", json=json)
|
||||
|
||||
def get_zoom(self):
|
||||
return self.get("/camera/zoom")
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import logging
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
from api_client import APIconnection
|
||||
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
||||
|
||||
|
||||
class TestCapture(unittest.TestCase):
|
||||
def test_capture_config(self):
|
||||
connection = APIconnection(host="localhost", port=5000, api_ver="v1")
|
||||
config = connection.get_config()
|
||||
|
||||
expected_keys = ["image_resolution", "stream_resolution", "numpy_resolution"]
|
||||
|
||||
for key in expected_keys:
|
||||
self.assertTrue(key in config)
|
||||
|
||||
def test_capture_videoport(self):
|
||||
connection = APIconnection(host="localhost", port=5000, api_ver="v1")
|
||||
resolution = connection.get_config()["stream_resolution"]
|
||||
|
||||
for resize in [None, (640, 480)]:
|
||||
|
||||
if resize:
|
||||
resolution = resize
|
||||
|
||||
capture_array = connection.capture(
|
||||
use_video_port=True,
|
||||
keep_on_disk=False,
|
||||
delete_after_use=True,
|
||||
resize=resize,
|
||||
)
|
||||
|
||||
self.assertTrue(capture_array.shape == (resolution[1], resolution[0], 3))
|
||||
|
||||
def test_capture_full(self):
|
||||
connection = APIconnection(host="localhost", port=5000, api_ver="v1")
|
||||
resolution = connection.get_config()["image_resolution"]
|
||||
|
||||
for resize in [None, (640, 480)]:
|
||||
|
||||
if resize:
|
||||
resolution = resize
|
||||
|
||||
capture_array = connection.capture(
|
||||
use_video_port=False,
|
||||
keep_on_disk=False,
|
||||
delete_after_use=True,
|
||||
resize=resize,
|
||||
)
|
||||
|
||||
self.assertTrue(capture_array.shape == (resolution[1], resolution[0], 3))
|
||||
|
||||
|
||||
class TestStage(unittest.TestCase):
|
||||
def test_stage_config(self):
|
||||
connection = APIconnection(host="localhost", port=5000, api_ver="v1")
|
||||
config = connection.get_config()
|
||||
|
||||
expected_keys = ["backlash"]
|
||||
|
||||
for key in expected_keys:
|
||||
self.assertTrue(key in config)
|
||||
|
||||
def test_stage_state(self):
|
||||
connection = APIconnection(host="localhost", port=5000, api_ver="v1")
|
||||
state = connection.get_state()
|
||||
|
||||
self.assertTrue("stage" in state)
|
||||
|
||||
expected_keys = ["position"]
|
||||
|
||||
for key in expected_keys:
|
||||
self.assertTrue(key in state["stage"])
|
||||
|
||||
def test_stage_movement(self):
|
||||
connection = APIconnection(host="localhost", port=5000, api_ver="v1")
|
||||
|
||||
move_distance = 500
|
||||
for axis in range(3):
|
||||
for direction in [1, -1]:
|
||||
pos_i_dict = connection.get_state()["stage"]["position"]
|
||||
pos_i = [pos_i_dict["x"], pos_i_dict["y"], pos_i_dict["z"]]
|
||||
|
||||
move = [0, 0, 0]
|
||||
move[axis] = move_distance * direction
|
||||
|
||||
connection.move_by(*move)
|
||||
|
||||
pos_f_dict = connection.get_state()["stage"]["position"]
|
||||
pos_f = [pos_f_dict["x"], pos_f_dict["y"], pos_f_dict["z"]]
|
||||
|
||||
diff = np.subtract(pos_f, pos_i)
|
||||
logging.debug("{} > {}".format(pos_i, pos_f))
|
||||
|
||||
self.assertTrue(np.array_equal(diff, move))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
suites = [
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestCapture),
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestStage),
|
||||
]
|
||||
|
||||
alltests = unittest.TestSuite(suites)
|
||||
|
||||
result = unittest.TextTestRunner(verbosity=2).run(alltests)
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from openflexure_microscope.camera.pi import CaptureObject, PiCameraStreamer
|
||||
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
||||
|
||||
|
||||
class TestCaptureMethods(unittest.TestCase):
|
||||
def test_still_capture(self):
|
||||
"""Tests capturing still images to a BytesIO stream."""
|
||||
global camera
|
||||
|
||||
for use_video_port in [True, False]:
|
||||
for resize in [None, (640, 480)]:
|
||||
|
||||
# Wait for camera
|
||||
camera.wait_for_camera()
|
||||
|
||||
# Capture to a context (auto-deletes files when done)
|
||||
with camera.new_image() as output:
|
||||
|
||||
camera.capture(output, use_video_port=use_video_port, resize=resize)
|
||||
|
||||
# Ensure file deletion fails and returns False
|
||||
self.assertFalse(output.delete_file())
|
||||
# Ensure capture not stored to file
|
||||
self.assertFalse(os.path.isfile(output.file))
|
||||
|
||||
# BEFORE DELETE: Ensure StreamObject 'stream' has
|
||||
# a valid BytesIO object and byte string
|
||||
self.assertTrue(isinstance(output.data, io.IOBase))
|
||||
self.assertTrue(isinstance(output.binary, (bytes, bytearray)))
|
||||
|
||||
# Save capture to file
|
||||
output.save_file()
|
||||
# Check file got saved
|
||||
self.assertTrue(os.path.isfile(output.file))
|
||||
|
||||
# Delete file
|
||||
output.delete_file()
|
||||
# Check file got deleted
|
||||
self.assertFalse(os.path.isfile(output.file))
|
||||
|
||||
# AFTER DELETE: Ensure StreamObject 'stream' has
|
||||
# a valid BytesIO object and byte string
|
||||
self.assertTrue(isinstance(output.data, io.IOBase))
|
||||
self.assertTrue(isinstance(output.binary, (bytes, bytearray)))
|
||||
|
||||
# Create a PIL image from stream
|
||||
image = Image.open(output.data)
|
||||
|
||||
# Ensure a valid PIL image was created
|
||||
self.assertTrue(isinstance(image, Image.Image))
|
||||
|
||||
# Calculate expected dimensions
|
||||
if resize:
|
||||
dims = resize
|
||||
else:
|
||||
if use_video_port:
|
||||
dims = camera.stream_resolution
|
||||
else:
|
||||
dims = camera.image_resolution
|
||||
|
||||
# Ensure PIL image size matches expected size
|
||||
print(image.size, dims)
|
||||
self.assertTrue(image.size == dims)
|
||||
|
||||
def test_still_store(self):
|
||||
"""Tests capturing still images to a file on disk."""
|
||||
global camera
|
||||
|
||||
for use_video_port in [True, False]:
|
||||
for resize in [None, (640, 480)]:
|
||||
# Wait for camera
|
||||
camera.wait_for_camera()
|
||||
|
||||
# Capture
|
||||
output = camera.capture(
|
||||
camera.new_image().file,
|
||||
use_video_port=use_video_port,
|
||||
resize=resize,
|
||||
)
|
||||
|
||||
# Check file got saved
|
||||
self.assertTrue(os.path.isfile(output.file))
|
||||
statinfo = os.stat(output.file)
|
||||
self.assertTrue(statinfo.st_size > 0)
|
||||
|
||||
# Ensure StreamObject 'stream' has
|
||||
# a valid BytesIO object and byte string
|
||||
self.assertTrue(isinstance(output.data, io.IOBase))
|
||||
self.assertTrue(isinstance(output.binary, (bytes, bytearray)))
|
||||
|
||||
# Ensure file deletion completes and returns True
|
||||
self.assertTrue(output.delete_file())
|
||||
|
||||
# Check file got deleted
|
||||
self.assertFalse(os.path.isfile(output.file))
|
||||
|
||||
|
||||
class TestUnencodedMethods(unittest.TestCase):
|
||||
def test_yuv_array(self):
|
||||
"""Tests capturing unencoded YUV data to a Numpy array."""
|
||||
global camera
|
||||
|
||||
for use_video_port in [True, False]:
|
||||
for resize in [None, (640, 480)]:
|
||||
|
||||
print("{}, {}, {}".format(id, resize, use_video_port))
|
||||
|
||||
# Wait for camera
|
||||
camera.wait_for_camera()
|
||||
|
||||
# Capture RGB array
|
||||
yuv = camera.yuv(use_video_port=use_video_port, resize=resize)
|
||||
|
||||
# Ensure capture output is a valid numpy array
|
||||
self.assertTrue(isinstance(yuv, np.ndarray))
|
||||
|
||||
# Calculate expected dimensions
|
||||
if resize:
|
||||
dims = resize
|
||||
else:
|
||||
if use_video_port:
|
||||
dims = camera.stream_resolution
|
||||
else:
|
||||
dims = camera.numpy_resolution
|
||||
|
||||
# Ensure array shape matches expected dimensions
|
||||
self.assertTrue(yuv.shape == (dims[1], dims[0], 3))
|
||||
|
||||
def test_rgb_array(self):
|
||||
"""Tests capturing unencoded YUV/RGB data to a Numpy array."""
|
||||
global camera
|
||||
|
||||
for use_video_port in [True, False]:
|
||||
for resize in [None, (640, 480)]:
|
||||
|
||||
print("{}, {}, {}".format(id, resize, use_video_port))
|
||||
|
||||
# Wait for camera
|
||||
camera.wait_for_camera()
|
||||
|
||||
# Capture RGB array
|
||||
rgb = camera.array(use_video_port=use_video_port, resize=resize)
|
||||
|
||||
# Ensure capture output is a valid numpy array
|
||||
self.assertTrue(isinstance(rgb, np.ndarray))
|
||||
|
||||
# Calculate expected dimensions
|
||||
if resize:
|
||||
dims = resize
|
||||
else:
|
||||
if use_video_port:
|
||||
dims = camera.stream_resolution
|
||||
else:
|
||||
dims = camera.numpy_resolution
|
||||
|
||||
# Ensure array shape matches expected dimensions
|
||||
self.assertTrue(rgb.shape == (dims[1], dims[0], 3))
|
||||
|
||||
|
||||
class TestRecordMethods(unittest.TestCase):
|
||||
def test_video_record(self):
|
||||
"""Tests recording videos to BytesIO stream, and to file on disk."""
|
||||
global camera
|
||||
|
||||
# Wait for camera
|
||||
camera.wait_for_camera()
|
||||
|
||||
with camera.new_video() as output:
|
||||
|
||||
# Start recording
|
||||
camera.start_recording(output)
|
||||
|
||||
# Record for 2 seconds
|
||||
time.sleep(2)
|
||||
# Stop recording
|
||||
camera.stop_recording()
|
||||
|
||||
# Check stream
|
||||
self.assertTrue(isinstance(output.data, io.IOBase))
|
||||
self.assertTrue(isinstance(output.binary, (bytes, bytearray)))
|
||||
|
||||
# Check file
|
||||
statinfo = os.stat(output.file)
|
||||
self.assertTrue(statinfo.st_size > 0)
|
||||
|
||||
# Log path
|
||||
temp_path = output.file
|
||||
|
||||
# Check file got deleted on __exit__
|
||||
self.assertFalse(os.path.isfile(temp_path))
|
||||
|
||||
time.sleep(0.25)
|
||||
|
||||
def test_video_store(self):
|
||||
"""Tests recording videos to file on disk, without context manager."""
|
||||
global camera
|
||||
|
||||
# Wait for camera
|
||||
camera.wait_for_camera()
|
||||
|
||||
# Start recording
|
||||
output = camera.start_recording(camera.new_video())
|
||||
|
||||
# Record for 2 seconds
|
||||
time.sleep(2)
|
||||
# Stop recording
|
||||
camera.stop_recording()
|
||||
|
||||
# Check file
|
||||
statinfo = os.stat(output.file)
|
||||
self.assertTrue(statinfo.st_size > 0)
|
||||
|
||||
# Ensure file deletion completes and returns True
|
||||
self.assertTrue(output.delete_file())
|
||||
|
||||
# Check file got deleted
|
||||
self.assertFalse(os.path.isfile(output.file))
|
||||
|
||||
|
||||
class TestThreadStarting(unittest.TestCase):
|
||||
def test_restarting_stream(self):
|
||||
"""Tests that a capture call restarts the camera worker thread."""
|
||||
global camera
|
||||
|
||||
# Wait for camera
|
||||
camera.wait_for_camera()
|
||||
|
||||
# Force-stop the camera worker thread (should return True)
|
||||
self.assertTrue(camera.stop_worker())
|
||||
|
||||
# Check Pi camera has been disconnected
|
||||
self.assertTrue(camera.camera)
|
||||
|
||||
# Restart worker thread
|
||||
self.assertTrue(camera.start_worker())
|
||||
|
||||
self.assertTrue(camera.camera)
|
||||
self.assertTrue(camera.thread)
|
||||
self.assertIsInstance(camera.stream, io.IOBase)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with PiCameraStreamer() as camera:
|
||||
|
||||
suites = [
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestCaptureMethods),
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestUnencodedMethods),
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestThreadStarting),
|
||||
unittest.TestLoader().loadTestsFromTestCase(TestRecordMethods),
|
||||
]
|
||||
|
||||
alltests = unittest.TestSuite(suites)
|
||||
|
||||
result = unittest.TextTestRunner(verbosity=2).run(alltests)
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import atexit
|
||||
import logging
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from openflexure_stage import OpenFlexureStage
|
||||
|
||||
from openflexure_microscope import Microscope, config
|
||||
from openflexure_microscope.camera.pi import PiCameraStreamer
|
||||
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
||||
|
||||
|
||||
class TestPluginMethods(unittest.TestCase):
|
||||
def test_plugin_load(self):
|
||||
plugin_arr = microscope.plugins.plugins
|
||||
|
||||
plugin_names = [plugin[0] for plugin in plugin_arr]
|
||||
|
||||
self.assertTrue("testing" in plugin_names)
|
||||
|
||||
def test_camera_access(self):
|
||||
identify = microscope.plugins.testing.identify()
|
||||
self.assertTrue(identify[0] is microscope.camera)
|
||||
|
||||
def test_stage_access(self):
|
||||
identify = microscope.plugins.testing.identify()
|
||||
self.assertTrue(identify[1] is microscope.stage)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
with Microscope() as microscope:
|
||||
|
||||
microscope.attach(PiCameraStreamer(), OpenFlexureStage())
|
||||
|
||||
microscope.plugins.attach("openflexure_microscope.plugins.testing:Plugin")
|
||||
|
||||
suites = [unittest.TestLoader().loadTestsFromTestCase(TestPluginMethods)]
|
||||
|
||||
alltests = unittest.TestSuite(suites)
|
||||
|
||||
result = unittest.TextTestRunner(verbosity=2).run(alltests)
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import logging
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
from openflexure_stage import OpenFlexureStage
|
||||
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
||||
|
||||
|
||||
class TestMicroscope(unittest.TestCase):
|
||||
def test_movement(self):
|
||||
move_distance = 500
|
||||
for axis in range(3):
|
||||
for direction in [1, -1]:
|
||||
pos_i = stage.position
|
||||
logging.debug(pos_i)
|
||||
|
||||
logging.info(
|
||||
"Moving axis {} by {}".format(axis, move_distance * direction)
|
||||
)
|
||||
move = [0, 0, 0]
|
||||
move[axis] = move_distance * direction
|
||||
|
||||
stage.move_rel(move)
|
||||
|
||||
pos_f = stage.position
|
||||
diff = np.subtract(pos_f, pos_i)
|
||||
logging.debug("{} > {}".format(pos_i, pos_f))
|
||||
|
||||
self.assertTrue(np.array_equal(diff, move))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with OpenFlexureStage("/dev/ttyUSB0") as stage:
|
||||
|
||||
suites = [unittest.TestLoader().loadTestsFromTestCase(TestMicroscope)]
|
||||
|
||||
alltests = unittest.TestSuite(suites)
|
||||
|
||||
result = unittest.TextTestRunner(verbosity=2).run(alltests)
|
||||
Loading…
Add table
Add a link
Reference in a new issue