Commit 31eb4bb9 authored by Josef Brandt's avatar Josef Brandt

FakeFilter stores generated particles on disk

plus some bugfixes 
parent ce63a986
......@@ -20,10 +20,14 @@ If not, see <https://www.gnu.org/licenses/>.
Image Generator for fake images for testing with simulated instrument interfaces
"""
import os
import sys
from PyQt5 import QtWidgets, QtCore
import cv2
import numpy as np
import noise
from copy import deepcopy
import pickle
from typing import Tuple, List
......@@ -127,6 +131,11 @@ class FakeCamera:
self.currentImage: np.ndarray = np.zeros((self.imgDims[1], self.imgDims[0], 3))
self.fakeFilter: FakeFilter = FakeFilter()
def close(self) -> None:
self.fakeFilter.saveToFile()
if self.fakeFilter.app is not None:
self.fakeFilter.app.deleteLater()
def updateImageAtPosition(self, position: List[float]) -> None:
"""
Centers the camera at the given µm coordinates and returns the camera image centered at this position.
......@@ -184,20 +193,67 @@ class FakeCamera:
class FakeFilter:
numIndParticles: int = 1 # number of individual particles presets to generate
numBlurSteps: int = 1 # number of blur steps to simulate (additionally to image without any blur)
maxBlurSize: int = 51 # highest blur radius
baseImageSize: int = 10
def __init__(self):
self.numParticles: int = 500
self.xRange: Tuple[float, float] = (-3000, 3000) # min and max of x Dimensions (in µm)
self.yRange: Tuple[float, float] = (-3000, 3000) # min and max of x Dimensions (in µm)
self.particleSizeRange: Tuple[float, float] = (5, 50) # min and max of particle radius (in µm)
self.presetParticles: List[dict] = self._generagePresetParticles()
self.numIndParticles: int = 10 # number of individual particles presets to generate
self.numBlurSteps: int = 7 # number of blur steps to simulate (additionally to image without any blur)
self.maxBlurSize: int = 51 # highest blur radius
self.baseImageSize: int = 100
self.presetParticles: List[dict] = []
self.particles: List[FakeParticle] = []
self.filePath: str = ''
self.app: QtWidgets.QApplication = None
self._updateFromFile()
def _updateFromFile(self) -> None:
def generateNew() -> None:
self.presetParticles = self._generatePresetParticles()
self._generateParticles()
path: str = self._getFilePath()
if not os.path.exists(path):
generateNew()
else:
with open(path, "rb") as fp:
savedFilter: FakeFilter = pickle.load(fp)
if self._isDifferentTo(savedFilter):
generateNew()
else:
self.particles = savedFilter.particles
self.presetParticles = savedFilter.presetParticles
def _isDifferentTo(self, otherFilter) -> bool:
isDifferent: bool = False
otherFilter: FakeFilter = otherFilter
for key in otherFilter.__dict__.keys():
if key not in ['app', 'presetParticles', 'particles']:
if otherFilter.__dict__[key] != self.__dict__[key]:
isDifferent = True
break
return isDifferent
def saveToFile(self) -> None:
fpath: str = self._getFilePath()
# QApplication cannot be pickled, so we replace it temporarily with None
origApp = self.app
self.app = None
with open(fpath, "wb") as fp:
pickle.dump(self, fp, protocol=-1)
self.app = origApp
def _getFilePath(self) -> str:
if self.filePath == '':
if self.app is None:
self.app = QtWidgets.QApplication(sys.argv) # has to be an instance attribute :/
self.app.setApplicationName("GEPARD")
path: str = QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppLocalDataLocation)
self.filePath = os.path.join(path, 'fakeFilter.pkl')
return self.filePath
def getParticleImage(self, particle: FakeParticle, blurRadius: int = 0) -> np.ndarray:
def getSizeCorrectedBlurRadius(blurRad: int, scaling: float) -> int:
"""If the images are scaled down later, we want to pick a stronger blurred image at this point"""
......@@ -219,7 +275,7 @@ class FakeFilter:
return cv2.resize(img, None, fx=scaleFac, fy=scaleFac)
def _generagePresetParticles(self) -> List[dict]:
def _generatePresetParticles(self) -> List[dict]:
"""
The list contains a dict for each particle variation, containing the image in different blur stages.
The key of that dicts is the used blur radius
......
......@@ -67,6 +67,7 @@ class SimulatedStage(object):
self.saveConfigToFile()
if self.uiEnabled:
self.ui.setEnabled(False)
self.camera.close()
def updateConfigFromFile(self) -> None:
if os.path.exists(self.filepath):
......
......@@ -34,7 +34,7 @@ class SimulatedRaman(RamanBase):
ramanParameters = {}
simulatedInterface = True
def __init__(self, logger, ui: bool = True):
def __init__(self, logger, ui: bool = False):
super().__init__()
self.name = 'SimulatedRaman'
self.logger = logger
......
......@@ -160,11 +160,10 @@ class SpectraHandler(object):
closestIndex = np.argmin(abs(spec[:, 0] - wavenumber))
return spec[closestIndex, 1]
if self.ramanctrlname == "ThermoFTIRCom":
if self.wavenumbers is None:
self.wavenumbers = spec[:, 0]
np.save(self.getPathForWavenumbers(), self.wavenumbers)
else:
if not np.array_equal(spec[:, 0], self.wavenumbers):
corrSpec: np.ndarray = np.zeros((self.wavenumbers.shape[0], 2))
corrSpec[:, 0] = self.wavenumbers
......@@ -205,7 +204,6 @@ class SpectraHandler(object):
return spectrum
def getPathForBackroundOfID(self, id: int) -> str:
assert self.tmpspecpath is not None, 'temp spec path not set for SpecHandler!'
return os.path.join(self.tmpspecpath, f'background {id}.npy')
......
......@@ -56,8 +56,11 @@ class SampleView(QtWidgets.QGraphicsView):
self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorViewCenter)
self.ramanctrl = RamanControl(self.logger)
self.simulatedRaman = simulatedRaman
if simulatedRaman:
self.ramanctrl = RamanControl(self.logger, ui=True)
else:
self.ramanctrl = RamanControl(self.logger)
self.ramanSwitchNeeded = False
if self.ramanctrl.name == 'RenishawCOM':
#determine, if ramanSwitch is needed:
......
from unittest import TestCase
from unittest.mock import MagicMock
import os
import json
import numpy as np
import tempfile
from typing import Tuple
from copy import deepcopy
from ..ramancom.simulated.simulatedStage import SimulatedStage
from ..ramancom.simulated.imageGenerator import FakeCamera, FakeFilter, FakeParticle, OverlapRange, getParticleOverlapRanges
class TestSimulatedStage(TestCase):
def setUp(self) -> None:
FakeFilter.numBlurSteps = 1
FakeFilter.numIndParticles = 1
FakeFilter.maxBlurSize = 1
FakeFilter.baseImageSize = 10
self.simulatedStage: SimulatedStage = SimulatedStage(ui=False)
self.simulatedStage.saveConfigToFile()
def tearDown(self) -> None:
self.simulatedStage.disconnect()
def test_config_was_saved(self):
self.assertTrue(os.path.exists(self.simulatedStage.filepath))
with open(self.simulatedStage.filepath, 'r') as fp:
......@@ -129,3 +131,71 @@ class Test_FakeCamera(TestCase):
self.assertEqual(ranges.subEndY, 50)
verifyShapeConsistency(fullImg, subImg, ranges)
class TestFakeFilter(TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.tmpDir: tempfile.TemporaryDirectory = tempfile.TemporaryDirectory()
def setUp(self) -> None:
self.fakeFilter: FakeFilter = FakeFilter()
self.fakeFilter.filePath = os.path.join(self.tmpDir.name, 'fakeFilter.pkl')
def tearDown(self) -> None:
if self.fakeFilter.app is not None:
self.fakeFilter.app.deleteLater()
@classmethod
def tearDownClass(cls) -> None:
cls.tmpDir.cleanup()
def test_saveConfig(self):
filterPath: str = self.fakeFilter._getFilePath()
self.fakeFilter.saveToFile()
self.assertTrue(os.path.exists(filterPath))
def test_updateFromFile(self):
self.fakeFilter.app = None
origFilter: FakeFilter = deepcopy(self.fakeFilter)
origParticleList: list = [1, 2, 3]
newParticleList: list = [4, 5, 6]
self.fakeFilter.particles = origParticleList
self.fakeFilter.saveToFile()
self.fakeFilter._generateParticles = MagicMock()
self.fakeFilter._generatePresetParticles = MagicMock()
self.fakeFilter.particles = newParticleList
self.fakeFilter._updateFromFile()
self.fakeFilter._generateParticles.assert_not_called()
self.fakeFilter._generatePresetParticles.assert_not_called()
self.assertEqual(self.fakeFilter.particles, origParticleList) # i.e., the particle List from the saved filte was loaded
self.fakeFilter.numBlurSteps += 1
self.fakeFilter._updateFromFile()
self.fakeFilter._generateParticles.assert_called_once()
self.fakeFilter._generatePresetParticles.assert_called_once()
origFilter.saveToFile()
def test_isDifferentTo(self):
otherFilter: FakeFilter = FakeFilter()
otherFilter.filePath = os.path.join(self.tmpDir.name, 'fakeFilter.pkl')
self.assertFalse(self.fakeFilter._isDifferentTo(otherFilter))
otherFilter.numBlurSteps += 1
self.assertTrue(self.fakeFilter._isDifferentTo(otherFilter))
otherFilter.numBlurSteps -= 1
otherFilter.numIndParticles += 1
self.assertTrue(self.fakeFilter._isDifferentTo(otherFilter))
otherFilter.numIndParticles -= 1
otherFilter.numParticles += 1
self.assertTrue(self.fakeFilter._isDifferentTo(otherFilter))
otherFilter.numParticles -= 1
otherFilter.maxBlurSize += 1
self.assertTrue(self.fakeFilter._isDifferentTo(otherFilter))
otherFilter.maxBlurSize -= 1
self.assertFalse(self.fakeFilter._isDifferentTo(otherFilter))
......@@ -3,14 +3,15 @@ import logging
import numpy as np
import os
import shutil
from ramancom.thermoFTIRCom import ThermoFTIRCom
from ramancom.specHandler import SpectraHandler
from ..ramancom.simulated.simulatedraman import SimulatedRaman
from ..ramancom.specHandler import SpectraHandler
class TestSpecHandler(unittest.TestCase):
def setUp(self) -> None:
logger = logging.getLogger('TestLogger')
self.specHandler: SpectraHandler = SpectraHandler(ThermoFTIRCom(logger))
self.simRaman: SimulatedRaman = SimulatedRaman(logger)
self.specHandler: SpectraHandler = SpectraHandler(self.simRaman)
self.specHandler.setDatasetPath(os.getcwd())
self.assertEqual(self.specHandler.dsetpath, os.getcwd())
specPath: str = os.path.join(os.getcwd(), 'spectra')
......@@ -19,6 +20,7 @@ class TestSpecHandler(unittest.TestCase):
def tearDown(self) -> None:
shutil.rmtree(self.specHandler.tmpspecpath)
self.simRaman.disconnect()
def test_map_spectra(self):
wavenumbers: np.ndarray = np.arange(100)
......
......@@ -142,6 +142,7 @@ class ModeHandler(object):
def closeAll(self) -> None:
for mode in [self.oscanMode, self.detectMode, self.specScanMode]:
if mode.widget is not None:
mode.widget.close()
def mousePressed(self, mouseEvent: QtGui.QMouseEvent) -> bool:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment