814 lines
31 KiB
Python
814 lines
31 KiB
Python
# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
|
|
# All rights reserved.
|
|
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted for academic and research use only
|
|
# (subject to the limitations in the disclaimer below)
|
|
# provided that the following conditions are met:
|
|
|
|
# * Redistributions of source code must retain the above copyright notice,
|
|
# this list of conditions and the following disclaimer.
|
|
|
|
# * Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
|
|
# * Neither the name of the copyright holders nor the names of its
|
|
# contributors may be used to endorse or promote products derived from this
|
|
# software without specific prior written permission.
|
|
|
|
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
|
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
|
|
from navigate.controller.sub_controllers.camera_view import CameraViewController
|
|
import pytest
|
|
import random
|
|
from unittest.mock import MagicMock
|
|
import numpy as np
|
|
|
|
|
|
class TestCameraViewController:
|
|
@pytest.fixture(autouse=True)
|
|
def setup_class(self, dummy_controller):
|
|
c = dummy_controller
|
|
self.v = dummy_controller.view
|
|
c.model = MagicMock()
|
|
c.model.get_offset_variance_maps = MagicMock(return_value=[None, None])
|
|
|
|
self.camera_view = CameraViewController(self.v.camera_waveform.camera_tab, c)
|
|
|
|
self.microscope_state = {
|
|
"channels": {
|
|
"channel_1": {
|
|
"is_selected": True,
|
|
"laser": "488nm",
|
|
"laser_index": 0,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 1.0,
|
|
"defocus": 100.0,
|
|
"filter_wheel_0": "Empty-Alignment",
|
|
"filter_position_0": 0,
|
|
"filter_wheel_1": "Empty-Alignment",
|
|
"filter_position_1": 0,
|
|
},
|
|
"channel_2": {
|
|
"is_selected": True,
|
|
"laser": "488nm",
|
|
"laser_index": 0,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 1.0,
|
|
"defocus": 100.0,
|
|
"filter_wheel_0": "Empty-Alignment",
|
|
"filter_position_0": 0,
|
|
"filter_wheel_1": "Empty-Alignment",
|
|
"filter_position_1": 0,
|
|
},
|
|
"channel_3": {
|
|
"is_selected": True,
|
|
"laser": "488nm",
|
|
"laser_index": 0,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 1.0,
|
|
"defocus": 100.0,
|
|
"filter_wheel_0": "Empty-Alignment",
|
|
"filter_position_0": 0,
|
|
"filter_wheel_1": "Empty-Alignment",
|
|
"filter_position_1": 0,
|
|
},
|
|
},
|
|
"number_z_steps": np.random.randint(10, 100),
|
|
"stack_cycling_mode": "per_stack",
|
|
"image_mode": "z-stack",
|
|
}
|
|
|
|
def test_init(self):
|
|
|
|
assert isinstance(self.camera_view, CameraViewController)
|
|
|
|
def test_update_display_state(self):
|
|
pass
|
|
|
|
def test_get_absolute_position(self, monkeypatch):
|
|
def mock_winfo_pointerx():
|
|
self.x = int(random.random())
|
|
return self.x
|
|
|
|
def mock_winfo_pointery():
|
|
self.y = int(random.random())
|
|
return self.y
|
|
|
|
monkeypatch.setattr(self.v, "winfo_pointerx", mock_winfo_pointerx)
|
|
monkeypatch.setattr(self.v, "winfo_pointery", mock_winfo_pointery)
|
|
|
|
# call the function under test
|
|
x, y = self.camera_view.get_absolute_position()
|
|
|
|
# make assertions about the return value
|
|
assert x == self.x
|
|
assert y == self.y
|
|
|
|
def test_popup_menu(self, monkeypatch):
|
|
|
|
# create a fake event object
|
|
self.startx = int(random.random())
|
|
self.starty = int(random.random())
|
|
event = type("Event", (object,), {"x": self.startx, "y": self.starty})()
|
|
self.grab_released = False
|
|
self.x = int(random.random())
|
|
self.y = int(random.random())
|
|
self.absx = int(random.random())
|
|
self.absy = int(random.random())
|
|
|
|
# monkey patch the get_absolute_position method to return specific values
|
|
def mock_get_absolute_position():
|
|
self.absx = int(random.random())
|
|
self.absy = int(random.random())
|
|
return self.absx, self.absy
|
|
|
|
monkeypatch.setattr(
|
|
self.camera_view, "get_absolute_position", mock_get_absolute_position
|
|
)
|
|
|
|
def mock_tk_popup(x, y):
|
|
self.x = x
|
|
self.y = y
|
|
|
|
def mock_grab_release():
|
|
self.grab_released = True
|
|
|
|
monkeypatch.setattr(self.camera_view.menu, "tk_popup", mock_tk_popup)
|
|
monkeypatch.setattr(self.camera_view.menu, "grab_release", mock_grab_release)
|
|
|
|
# call the function under test
|
|
self.camera_view.popup_menu(event)
|
|
|
|
# make assertions about the state of the view object
|
|
assert self.camera_view.move_to_x == self.startx
|
|
assert self.camera_view.move_to_y == self.starty
|
|
assert self.x == self.absx
|
|
assert self.y == self.absy
|
|
assert self.grab_released is True
|
|
|
|
@pytest.mark.parametrize("name", ["minmax", "image"])
|
|
@pytest.mark.parametrize("data", [[random.randint(0, 49), random.randint(50, 100)]])
|
|
def test_initialize(self, name, data):
|
|
|
|
self.camera_view.initialize(name, data)
|
|
|
|
# Checking values
|
|
if name == "minmax":
|
|
assert self.camera_view.image_palette["Min"].get() == data[0]
|
|
assert self.camera_view.image_palette["Max"].get() == data[1]
|
|
if name == "image":
|
|
assert self.camera_view.image_metrics["Frames"].get() == data[0]
|
|
|
|
def test_set_mode(self):
|
|
|
|
# Test default mode
|
|
self.camera_view.set_mode()
|
|
assert self.camera_view.mode == ""
|
|
assert self.camera_view.menu.entrycget("Move Here", "state") == "disabled"
|
|
|
|
# Test 'live' mode
|
|
self.camera_view.set_mode("live")
|
|
assert self.camera_view.mode == "live"
|
|
assert self.camera_view.menu.entrycget("Move Here", "state") == "normal"
|
|
|
|
# Test 'stop' mode
|
|
self.camera_view.set_mode("stop")
|
|
assert self.camera_view.mode == "stop"
|
|
assert self.camera_view.menu.entrycget("Move Here", "state") == "normal"
|
|
|
|
# Test invalid mode
|
|
self.camera_view.set_mode("invalid")
|
|
assert self.camera_view.mode == "invalid"
|
|
assert self.camera_view.menu.entrycget("Move Here", "state") == "disabled"
|
|
|
|
@pytest.mark.parametrize("mode", ["stop", "live"])
|
|
def test_move_stage(self, mode):
|
|
|
|
# Setup to check formula inside func is correct
|
|
microscope_name = self.camera_view.parent_controller.configuration[
|
|
"experiment"
|
|
]["MicroscopeState"]["microscope_name"]
|
|
zoom_value = self.camera_view.parent_controller.configuration["experiment"][
|
|
"MicroscopeState"
|
|
]["zoom"]
|
|
pixel_size = self.camera_view.parent_controller.configuration["configuration"][
|
|
"microscopes"
|
|
][microscope_name]["zoom"]["pixel_size"][zoom_value]
|
|
|
|
current_center_x = (
|
|
self.camera_view.zoom_rect[0][0] + self.camera_view.zoom_rect[0][1]
|
|
) / 2
|
|
current_center_y = (
|
|
self.camera_view.zoom_rect[1][0] + self.camera_view.zoom_rect[1][1]
|
|
) / 2
|
|
|
|
self.camera_view.move_to_x = int(random.random())
|
|
self.camera_view.move_to_y = int(random.random())
|
|
|
|
# This is the formula to check
|
|
offset_x = (
|
|
(self.camera_view.move_to_x - current_center_x)
|
|
/ self.camera_view.zoom_scale
|
|
* self.camera_view.canvas_width_scale
|
|
* pixel_size
|
|
)
|
|
offset_y = (
|
|
(self.camera_view.move_to_y - current_center_y)
|
|
/ self.camera_view.zoom_scale
|
|
* self.camera_view.canvas_width_scale
|
|
* pixel_size
|
|
)
|
|
|
|
# Set the mode to check if statements
|
|
self.camera_view.mode = mode
|
|
|
|
# Act
|
|
self.camera_view.move_stage()
|
|
|
|
# Check
|
|
assert self.camera_view.parent_controller.pop() == "get_stage_position"
|
|
res = self.camera_view.parent_controller.pop()
|
|
if mode == "stop":
|
|
assert res == "move_stage_and_acquire_image"
|
|
else:
|
|
assert res == "move_stage_and_update_info"
|
|
# Checking that move stage properly changed pos
|
|
new_pos = self.camera_view.parent_controller.pop()
|
|
self.camera_view.parent_controller.stage_pos["x"] -= offset_x
|
|
self.camera_view.parent_controller.stage_pos["y"] += offset_y
|
|
assert new_pos == self.camera_view.parent_controller.stage_pos
|
|
|
|
def test_reset_display(self, monkeypatch):
|
|
|
|
# Mock process image function
|
|
process_image_called = False
|
|
|
|
def mock_process_image():
|
|
nonlocal process_image_called
|
|
process_image_called = True
|
|
|
|
monkeypatch.setattr(self.camera_view, "process_image", mock_process_image)
|
|
|
|
# Reset and check
|
|
self.camera_view.reset_display()
|
|
assert np.array_equal(
|
|
self.camera_view.zoom_rect,
|
|
np.array(
|
|
[
|
|
[0, self.camera_view.view.canvas_width],
|
|
[0, self.camera_view.view.canvas_height],
|
|
]
|
|
),
|
|
)
|
|
assert np.array_equal(self.camera_view.zoom_offset, np.array([[0], [0]]))
|
|
assert self.camera_view.zoom_value == 1
|
|
assert self.camera_view.zoom_scale == 1
|
|
assert self.camera_view.zoom_width == self.camera_view.view.canvas_width
|
|
assert self.camera_view.zoom_height == self.camera_view.view.canvas_height
|
|
assert process_image_called is True
|
|
|
|
def test_process_image(self):
|
|
self.camera_view.image = np.random.randint(0, 256, (600, 800))
|
|
self.camera_view.digital_zoom = MagicMock()
|
|
# self.camera_view.detect_saturation = MagicMock()
|
|
self.camera_view.down_sample_image = MagicMock()
|
|
self.camera_view.scale_image_intensity = MagicMock()
|
|
self.camera_view.add_crosshair = MagicMock()
|
|
self.camera_view.apply_lut = MagicMock()
|
|
self.camera_view.populate_image = MagicMock()
|
|
|
|
self.camera_view.process_image()
|
|
|
|
self.camera_view.digital_zoom.assert_called()
|
|
# self.camera_view.detect_saturation.assert_called()
|
|
self.camera_view.down_sample_image.assert_called()
|
|
self.camera_view.scale_image_intensity.assert_called()
|
|
self.camera_view.add_crosshair.assert_called()
|
|
self.camera_view.apply_lut.assert_called()
|
|
self.camera_view.populate_image.assert_called()
|
|
|
|
@pytest.mark.parametrize("num,value", [(4, 0.95), (5, 1.05)])
|
|
def test_mouse_wheel(self, num, value):
|
|
|
|
# Test zoom in and out
|
|
event = MagicMock()
|
|
event.num = num
|
|
event.x = int(random.random())
|
|
event.y = int(random.random())
|
|
event.delta = 120
|
|
self.camera_view.zoom_value = 1
|
|
self.camera_view.zoom_scale = 1
|
|
self.camera_view.zoom_width = self.camera_view.view.canvas_width
|
|
self.camera_view.zoom_height = self.camera_view.view.canvas_height
|
|
self.camera_view.reset_display = MagicMock()
|
|
self.camera_view.process_image = MagicMock()
|
|
self.camera_view.mouse_wheel(event)
|
|
|
|
assert self.camera_view.zoom_value == value
|
|
assert self.camera_view.zoom_scale == value
|
|
assert self.camera_view.zoom_width == self.camera_view.view.canvas_width / value
|
|
assert (
|
|
self.camera_view.zoom_height == self.camera_view.view.canvas_height / value
|
|
)
|
|
|
|
if (
|
|
self.camera_view.zoom_width > self.camera_view.view.canvas_width
|
|
or self.camera_view.zoom_height > self.camera_view.view.canvas_height
|
|
):
|
|
assert self.camera_view.reset_display.called is True
|
|
self.camera_view.process_image.assert_called()
|
|
else:
|
|
assert self.camera_view.reset_display.called is False
|
|
not self.camera_view.process_image.assert_called()
|
|
|
|
@pytest.mark.skip("AssertionError: Expected 'mock' to have been called.")
|
|
def test_digital_zoom(self):
|
|
|
|
# Setup
|
|
a, b, c, d, e, f = [random.randint(1, 100) for _ in range(6)]
|
|
g, h = [random.randint(100, 400) for _ in range(2)]
|
|
i, j = [random.randint(500, 1000) for _ in range(2)]
|
|
val, scale, widthsc, heightsc = [random.randint(2, 4) for _ in range(4)]
|
|
self.camera_view.zoom_rect = np.array([[a, b], [c, d]])
|
|
self.camera_view.zoom_offset = np.array([[e], [f]])
|
|
self.camera_view.zoom_value = val
|
|
self.camera_view.zoom_scale = scale
|
|
self.camera_view.zoom_width = g # 300
|
|
self.camera_view.zoom_height = h # 400
|
|
self.camera_view.view.canvas_width = i # 800
|
|
self.camera_view.view.canvas_height = j # 600
|
|
self.camera_view.canvas_width_scale = widthsc
|
|
self.camera_view.canvas_height_scale = heightsc
|
|
self.camera_view.image = np.random.randint(0, 256, (600, 800))
|
|
self.camera_view.reset_display = MagicMock()
|
|
|
|
# Preprocess
|
|
new_zoom_rec = self.camera_view.zoom_rect - self.camera_view.zoom_offset
|
|
new_zoom_rec *= self.camera_view.zoom_value
|
|
new_zoom_rec += self.camera_view.zoom_offset
|
|
|
|
x_start_index = int(-new_zoom_rec[0][0] / self.camera_view.zoom_scale)
|
|
x_end_index = int(x_start_index + self.camera_view.zoom_width)
|
|
y_start_index = int(-new_zoom_rec[1][0] / self.camera_view.zoom_scale)
|
|
y_end_index = int(y_start_index + self.camera_view.zoom_height)
|
|
|
|
crosshair_x = (new_zoom_rec[0][0] + new_zoom_rec[0][1]) / 2
|
|
crosshair_y = (new_zoom_rec[1][0] + new_zoom_rec[1][1]) / 2
|
|
if crosshair_x < 0 or crosshair_x >= self.camera_view.view.canvas_width:
|
|
crosshair_x = -1
|
|
if crosshair_y < 0 or crosshair_y >= self.camera_view.view.canvas_height:
|
|
crosshair_y = -1
|
|
|
|
new_image = self.camera_view.image[
|
|
y_start_index
|
|
* self.camera_view.canvas_height_scale : y_end_index
|
|
* self.camera_view.canvas_height_scale,
|
|
x_start_index
|
|
* self.camera_view.canvas_width_scale : x_end_index
|
|
* self.camera_view.canvas_width_scale,
|
|
]
|
|
|
|
# Call func
|
|
self.camera_view.digital_zoom()
|
|
|
|
# Check zoom rec
|
|
assert np.array_equal(self.camera_view.zoom_rect, new_zoom_rec)
|
|
# Check zoom offset
|
|
assert np.array_equal(self.camera_view.zoom_offset, np.array([[0], [0]]))
|
|
# Check zoom_value
|
|
assert self.camera_view.zoom_value == 1
|
|
# Check zoom_image
|
|
assert np.array_equal(self.camera_view.zoom_image, new_image)
|
|
# Check crosshairs
|
|
assert self.camera_view.crosshair_x == int(crosshair_x)
|
|
assert self.camera_view.crosshair_y == int(crosshair_y)
|
|
# Check reset display
|
|
self.camera_view.reset_display.assert_called()
|
|
|
|
@pytest.mark.parametrize("onoff", [True, False])
|
|
def test_left_click(self, onoff):
|
|
self.camera_view.add_crosshair = MagicMock()
|
|
self.camera_view.digital_zoom = MagicMock()
|
|
# self.camera_view.detect_saturation = MagicMock()
|
|
self.camera_view.down_sample_image = MagicMock()
|
|
self.camera_view.transpose_image = MagicMock()
|
|
self.camera_view.scale_image_intensity = MagicMock()
|
|
self.camera_view.apply_lut = MagicMock()
|
|
self.camera_view.populate_image = MagicMock()
|
|
event = MagicMock()
|
|
|
|
self.camera_view.image = np.random.randint(0, 256, (600, 800))
|
|
self.camera_view.apply_cross_hair = onoff
|
|
|
|
self.camera_view.left_click(event)
|
|
|
|
self.camera_view.add_crosshair.assert_called()
|
|
self.camera_view.populate_image.assert_called()
|
|
assert self.camera_view.apply_cross_hair == (not onoff)
|
|
|
|
@pytest.mark.parametrize("frames", [0, 1, 2, 10])
|
|
def test_update_max_count(self, frames):
|
|
self.camera_view.image_metrics["Frames"].set(frames)
|
|
|
|
idx = 0
|
|
temp = [0] * 40
|
|
|
|
while idx < 40:
|
|
self.camera_view._last_frame_display_max = np.random.randint(0, 500)
|
|
temp[idx] = self.camera_view._last_frame_display_max
|
|
idx += 1
|
|
# Act
|
|
self.camera_view.update_max_counts()
|
|
|
|
assert self.camera_view._max_intensity_history_idx == idx % 32
|
|
assert (
|
|
self.camera_view.max_intensity_history[
|
|
self.camera_view._max_intensity_history_idx - 1
|
|
]
|
|
== self.camera_view._last_frame_display_max
|
|
)
|
|
|
|
# Assert
|
|
if frames == 0:
|
|
assert self.camera_view.image_metrics["Frames"].get() == 1
|
|
frame_num = 1
|
|
else:
|
|
frame_num = frames
|
|
|
|
if frame_num <= idx:
|
|
rolling_average = sum(temp[idx - frame_num : idx]) / frame_num
|
|
else:
|
|
rolling_average = sum(temp[:idx]) / frame_num
|
|
|
|
assert self.camera_view.image_metrics["Image"].get() == round(
|
|
rolling_average, 0
|
|
)
|
|
|
|
def test_down_sample_image(self, monkeypatch):
|
|
import cv2
|
|
|
|
# create a test image
|
|
test_image = np.random.rand(100, 100)
|
|
self.zoom_image = test_image
|
|
|
|
# set the widget size
|
|
widget = type("MyWidget", (object,), {"widget": self.camera_view.view})
|
|
event = type(
|
|
"MyEvent",
|
|
(object,),
|
|
{
|
|
"widget": widget,
|
|
"width": np.random.randint(5, 1000),
|
|
"height": np.random.randint(5, 1000),
|
|
},
|
|
)
|
|
self.camera_view.resize(event)
|
|
|
|
# monkeypatch cv2.resize
|
|
def mocked_resize(src, dsize, interpolation=1):
|
|
return np.ones((dsize[0], dsize[1]))
|
|
|
|
monkeypatch.setattr(cv2, "resize", mocked_resize)
|
|
|
|
# call the function
|
|
down_sampled_image = self.camera_view.down_sample_image(test_image)
|
|
|
|
# assert that the image has been resized correctly
|
|
assert np.shape(down_sampled_image) == (
|
|
self.camera_view.view.canvas_width,
|
|
self.camera_view.view.canvas_height,
|
|
)
|
|
|
|
# assert that the image has not been modified
|
|
assert not np.array_equal(down_sampled_image, test_image)
|
|
|
|
@pytest.mark.parametrize("auto", [True, False])
|
|
def test_scale_image_intensity(self, auto):
|
|
# Create a test image
|
|
test_image = np.random.rand(100, 100)
|
|
|
|
# Set autoscale to True
|
|
self.camera_view.autoscale = auto
|
|
|
|
if auto is False:
|
|
self.camera_view.max_counts = 1.5
|
|
self.camera_view.min_counts = 0.5
|
|
|
|
# Call the function
|
|
scaled_image = self.camera_view.scale_image_intensity(test_image)
|
|
|
|
# Assert that max_counts and min_counts have been set correctly
|
|
if auto is True:
|
|
assert self.camera_view._last_frame_display_max == np.max(test_image)
|
|
|
|
# Assert that the image has been scaled correctly
|
|
assert np.min(scaled_image) >= 0
|
|
assert np.max(scaled_image) <= 255
|
|
|
|
def test_populate_image(self, monkeypatch):
|
|
from PIL import Image, ImageTk
|
|
import cv2
|
|
|
|
# Create test image
|
|
self.camera_view.cross_hair_image = np.random.rand(100, 100)
|
|
self.camera_view.ilastik_seg_mask = np.random.rand(100, 100)
|
|
|
|
# Set display_mask_flag to True
|
|
self.camera_view.display_mask_flag = True
|
|
|
|
# Monkeypatch the Image.fromarray() method of PIL
|
|
def mocked_fromarray(arr):
|
|
return arr
|
|
|
|
monkeypatch.setattr(Image, "fromarray", mocked_fromarray)
|
|
|
|
# Monkeypatch the cv2.resize() function
|
|
def mocked_resize(arr, size):
|
|
return arr
|
|
|
|
monkeypatch.setattr(cv2, "resize", mocked_resize)
|
|
|
|
# Monkeypatch the Image.blend() method of PIL
|
|
def mocked_blend(img1, img2, alpha):
|
|
return img1 * alpha + img2 * (1 - alpha)
|
|
|
|
monkeypatch.setattr(Image, "blend", mocked_blend)
|
|
|
|
def mocked_PhotoImage(img):
|
|
mock_photo = MagicMock()
|
|
# Set up width and height methods to return appropriate dimensions
|
|
mock_photo.width.return_value = 100
|
|
mock_photo.height.return_value = 100
|
|
return mock_photo
|
|
|
|
monkeypatch.setattr(ImageTk, "PhotoImage", mocked_PhotoImage)
|
|
|
|
self.camera_view.canvas.create_image = MagicMock()
|
|
self.camera_view.image_cache_flag = True
|
|
|
|
# Call the function
|
|
self.camera_view.populate_image(self.camera_view.cross_hair_image)
|
|
|
|
# Assert that the tk_image has been created correctly
|
|
assert self.camera_view._img_buf is not None
|
|
|
|
# Set display_mask_flag to True
|
|
self.camera_view.display_mask_flag = False
|
|
|
|
# Call the function
|
|
self.camera_view.populate_image(self.camera_view.cross_hair_image)
|
|
|
|
def test_initialize_non_live_display(self):
|
|
# Create test buffer and microscope_state
|
|
camera_parameters = {
|
|
"img_x_pixels": np.random.randint(1, 200),
|
|
"img_y_pixels": np.random.randint(1, 200),
|
|
}
|
|
|
|
# Call the function
|
|
self.camera_view.initialize_non_live_display(
|
|
self.microscope_state, camera_parameters
|
|
)
|
|
|
|
# Assert that the variables have been set correctly
|
|
assert self.camera_view.image_count == 0
|
|
assert self.camera_view.slice_index == 0
|
|
assert self.camera_view.number_of_channels == len(
|
|
self.microscope_state["channels"]
|
|
)
|
|
assert (
|
|
self.camera_view.number_of_slices == self.microscope_state["number_z_steps"]
|
|
)
|
|
assert (
|
|
self.camera_view.total_images_per_volume
|
|
== self.camera_view.number_of_channels * self.camera_view.number_of_slices
|
|
)
|
|
assert self.camera_view.original_image_width == int(
|
|
camera_parameters["img_x_pixels"]
|
|
)
|
|
assert self.camera_view.original_image_height == int(
|
|
camera_parameters["img_y_pixels"]
|
|
)
|
|
assert self.camera_view.canvas_width_scale == float(
|
|
self.camera_view.original_image_width / self.camera_view.canvas_width
|
|
)
|
|
assert self.camera_view.canvas_height_scale == float(
|
|
self.camera_view.original_image_height / self.camera_view.canvas_height
|
|
)
|
|
|
|
def test_identify_channel_index_and_slice(self):
|
|
# Not currently in use
|
|
pass
|
|
|
|
def test_retrieve_image_slice_from_volume(self):
|
|
# Not currently in use
|
|
pass
|
|
|
|
@pytest.mark.parametrize("transpose", [True, False])
|
|
def test_display_image(self, transpose):
|
|
"""Test the display of an image on the camera view
|
|
|
|
TODO: The recent refactor makes this test non-functional. It needs to be updated
|
|
The newer code does not use the camera_view.image attribute after the
|
|
transpose operation, so any transpose/image flipping is not reflected in the
|
|
final image. The test should be updated to reflect this.,
|
|
"""
|
|
|
|
self.camera_view.initialize_non_live_display(
|
|
self.microscope_state, {"img_x_pixels": 50, "img_y_pixels": 100}
|
|
)
|
|
self.camera_view.digital_zoom = MagicMock()
|
|
# self.camera_view.detect_saturation = MagicMock()
|
|
self.camera_view.down_sample_image = MagicMock()
|
|
self.camera_view.scale_image_intensity = MagicMock()
|
|
self.camera_view.apply_lut = MagicMock()
|
|
self.camera_view.populate_image = MagicMock()
|
|
images = np.random.rand(10, 100, 50)
|
|
|
|
self.camera_view.transpose = transpose
|
|
count = 0
|
|
self.camera_view.image_count = count
|
|
self.camera_view.image_metrics = {"Channel": MagicMock()}
|
|
self.camera_view.update_max_counts = MagicMock()
|
|
self.camera_view.flip_flags = {"x": False, "y": False}
|
|
|
|
image_id = np.random.randint(0, 10)
|
|
self.camera_view.try_to_display_image(images[image_id])
|
|
|
|
assert (
|
|
self.camera_view.spooled_images.size_y,
|
|
self.camera_view.spooled_images.size_x,
|
|
) == np.shape(images[image_id])
|
|
assert self.camera_view.image_count == count + 1
|
|
|
|
self.camera_view.flip_flags = {"x": True, "y": False}
|
|
image_id = np.random.randint(0, 10)
|
|
self.camera_view.try_to_display_image(images[image_id])
|
|
# assert np.shape(self.camera_view.image) == np.shape(images[image_id])
|
|
# if not transpose:
|
|
# assert (self.camera_view.image == images[image_id][:, ::-1]).all()
|
|
# else:
|
|
# assert (self.camera_view.image == images[image_id][:, ::-1].T).all()
|
|
assert self.camera_view.image_count == count + 2
|
|
|
|
self.camera_view.flip_flags = {"x": False, "y": True}
|
|
image_id = np.random.randint(0, 10)
|
|
self.camera_view.try_to_display_image(images[image_id])
|
|
# assert np.shape(self.camera_view.image) == np.shape(images[image_id])
|
|
# if not transpose:
|
|
# assert (self.camera_view.image == images[image_id][::-1, :]).all()
|
|
# else:
|
|
# assert (self.camera_view.image == images[image_id][::-1, :].T).all()
|
|
assert self.camera_view.image_count == count + 3
|
|
|
|
self.camera_view.flip_flags = {"x": True, "y": True}
|
|
image_id = np.random.randint(0, 10)
|
|
self.camera_view.try_to_display_image(images[image_id])
|
|
# assert np.shape(self.camera_view.image) == np.shape(images[image_id])
|
|
# if not transpose:
|
|
# assert (self.camera_view.image == images[image_id][::-1, ::-1]).all()
|
|
# else:
|
|
# assert (self.camera_view.image == images[image_id][::-1, ::-1].T).all()
|
|
assert self.camera_view.image_count == count + 4
|
|
|
|
def test_add_crosshair(self):
|
|
|
|
# Arrange
|
|
x = self.camera_view.canvas_width
|
|
y = self.camera_view.canvas_height
|
|
image = np.random.rand(x, y)
|
|
self.camera_view.apply_cross_hair = True
|
|
|
|
# Act
|
|
image2 = self.camera_view.add_crosshair(image)
|
|
|
|
# Assert
|
|
assert np.all(image2[:, self.camera_view.zoom_rect[0][1] // 2] == 255)
|
|
assert np.all(image2[self.camera_view.zoom_rect[1][1] // 2, :] == 255)
|
|
|
|
def test_apply_LUT(self):
|
|
# Someone else with better numpy understanding will need to do this TODO
|
|
|
|
pass
|
|
|
|
def test_update_LUT(self):
|
|
# Same as apply LUT TODO
|
|
pass
|
|
|
|
def test_toggle_min_max_button(self):
|
|
|
|
# Setup for true path
|
|
self.camera_view.image_palette["Autoscale"].set(True)
|
|
|
|
# Act by calling function
|
|
self.camera_view.toggle_min_max_buttons()
|
|
|
|
# Assert things are correct
|
|
assert str(self.camera_view.image_palette["Min"].widget["state"]) == "disabled"
|
|
assert str(self.camera_view.image_palette["Max"].widget["state"]) == "disabled"
|
|
|
|
# Setup for false path
|
|
self.camera_view.image_palette["Autoscale"].set(False)
|
|
|
|
# Mock function call to isolate
|
|
self.camera_view.update_min_max_counts = MagicMock()
|
|
|
|
# Act by calling function
|
|
self.camera_view.toggle_min_max_buttons()
|
|
|
|
# Assert things are correct and called
|
|
assert str(self.camera_view.image_palette["Min"].widget["state"]) == "normal"
|
|
assert str(self.camera_view.image_palette["Max"].widget["state"]) == "normal"
|
|
self.camera_view.update_min_max_counts.assert_called()
|
|
|
|
def test_transpose_image(self):
|
|
# Create test data
|
|
self.camera_view.image_palette["Flip XY"].set(True)
|
|
self.camera_view.transpose = None
|
|
|
|
# Call the function
|
|
self.camera_view.update_transpose_state()
|
|
|
|
# Assert the output
|
|
assert self.camera_view.transpose is True
|
|
|
|
# Create test data
|
|
self.camera_view.image_palette["Flip XY"].set(False)
|
|
self.camera_view.transpose = None
|
|
|
|
# Call the function
|
|
self.camera_view.update_transpose_state()
|
|
|
|
# Assert the output
|
|
assert self.camera_view.transpose is False
|
|
|
|
def test_update_min_max_counts(self):
|
|
# Create test data
|
|
min = np.random.randint(0, 10)
|
|
max = np.random.randint(0, 10)
|
|
self.camera_view.image_palette["Min"].set(min)
|
|
self.camera_view.image_palette["Max"].set(max)
|
|
|
|
self.camera_view.min_counts = None
|
|
self.camera_view.max_counts = None
|
|
|
|
# Call the function
|
|
self.camera_view.update_min_max_counts()
|
|
|
|
# Assert the output
|
|
assert self.camera_view.min_counts == min
|
|
assert self.camera_view.max_counts == max
|
|
|
|
def test_set_mask_color_table(self):
|
|
# This is beyond me currently TODO
|
|
pass
|
|
|
|
def test_display_mask(self, monkeypatch):
|
|
import cv2
|
|
|
|
# Create test data
|
|
self.camera_view.ilastik_seg_mask = None
|
|
self.camera_view.ilastik_mask_ready_lock.acquire()
|
|
mask = np.zeros((5, 5), dtype=np.uint8)
|
|
self.camera_view.mask_color_table = np.zeros((256, 1, 3), dtype=np.uint8)
|
|
|
|
# Define the monkeypatch
|
|
def mock_applyColorMap(mask, mask_color_table):
|
|
return mask
|
|
|
|
# Apply the monkeypatch
|
|
monkeypatch.setattr(cv2, "applyColorMap", mock_applyColorMap)
|
|
|
|
# Call the function
|
|
self.camera_view.display_mask(mask)
|
|
|
|
# Assert the output
|
|
assert (self.camera_view.ilastik_seg_mask == mask).all()
|
|
assert not self.camera_view.ilastik_mask_ready_lock.locked()
|
|
|
|
def test_update_canvas_size(self):
|
|
self.camera_view.view.canvas["width"] = random.randint(1, 2000)
|
|
self.camera_view.view.canvas["height"] = random.randint(1, 2000)
|
|
|
|
self.camera_view.update_canvas_size()
|
|
|
|
assert self.camera_view.canvas_width > 0
|
|
assert self.camera_view.canvas_height > 0
|