feat: init
This commit is contained in:
813
test/controller/sub_controllers/test_camera_view.py
Normal file
813
test/controller/sub_controllers/test_camera_view.py
Normal file
@@ -0,0 +1,813 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user