Skip to content

Pr/pre audit - #205

Open
iliasoroka1 wants to merge 69 commits into
masterfrom
pr/pre-audit
Open

Pr/pre audit#205
iliasoroka1 wants to merge 69 commits into
masterfrom
pr/pre-audit

Conversation

@iliasoroka1

Copy link
Copy Markdown

Feature and Algorithm Work Before the LLM-Assisted Audit

At the macro level I added experimental pipeline:

define plate
    → estimate a useful scan Z
    → scan the circular plate
    → detect and center a worm
    → switch camera settings
    → refine focus
    → start tracking
    → optionally record

Automated Plate Scanning

Plate geometry

To fit a plate I fit a circle using:

$$ x_i^2 + y_i^2 + D x_i + E y_i + F = 0. $$

And estimate the coefficients with least squares regression. The centre and radius are

$$ c_x=-\frac{D}{2}, \qquad c_y=-\frac{E}{2}, \qquad r=\sqrt{c_x^2+c_y^2-F}. $$

tile generation

I use calibrated camera field of view to calculate distances between scan
positions.

$$ \Delta x = \max(W(1-o_x), 10^{-3}), \qquad \Delta y = \max(H(1-o_y), 10^{-3}). $$

Then I reaine a tile only if its centre satisfies circle

$$ (x-c_x)^2 + (y-c_y)^2 \leq r^2. $$

While scanning, rows alternate between left to right and right to left

Scan-to-track transition

Once a worm is centred I do following

  • switch to the tracking/recording exposure, gain, and frame rate
  • starts live view
  • starts tracking and live autofocus
  • starts recording

I repeat the scan if a worm is not found

Autofocus and Focus-Plane Estimation

I changed the autofocus to be a peek seekeer rather then a PID

At each Z position it collects (B) frame-level focus scores:

$$ f_1,f_2,\ldots,f_B. $$

And calculates the median for that Z:

$$ P_t={median}(f_1,\ldots,f_B). $$

Until a all B frames are available we do not move z.

The autofocus begins with a configurable big step (s_0). If focus
decreases beyond a fraction for enough evaluations

$$ \bar{P}_t < \bar{P}_{t-1}(1-\epsilon), $$

we treat it as a overshoot, reverses direction, and
reduces the step

If focus falls below a configurable fraction (\rho) of the best observed
focus,

$$ \bar{P}_t < \rho P_{\text{best}}, $$

the autofocus treats focus as lost and restores the biggest step.

Fixed the smoothing weights originally used
min(1, n - 1) as their denominator, which I corrected to
max(1, n - 1).

I also added asyncio event loops for scan and autofocus workers.

Intensity-based scan Z

To get a correct Z position for scanning, I sample the mean image
intensity over a Z range:

$$ \mu(z_i)=\frac{1}{N}\sum_{p=1}^{N}I_p(z_i). $$

I then smooth the estimate and calculate its first derivative.

The implementation identifies:

$$ z_g = \underset{z}{{arg,max}}, \frac{d\mu}{dz}, $$

and the closest first-derivative zero crossing (z_0). When both are available,
the selected scan plane is

$$ z_{\text{scan}}=\frac{z_g+z_0}{2}. $$

Tracking Changes

Since there is no constant 16ms delay coming from USB port, I changed fixed waiting times to:

$$ t_{\text{ready}} = t_{\text{command}} + t_{\text{floor}} + T_{\text{travel}}(d_{\max}), $$

where (d*{\max}) is the largest movement requested in the current iteration.
The tracking worker then waits until the camera reports an image retrieval
timestamp newer than (t*{\text{ready}}).

I splitted velocity and speed stage configuration into:

  • fast and slow input
  • precision positioning
  • plate scanning
  • tracking

Since I once collided the camera with the X stage, I added a rule for the unsafe combination of low Y and high Z.

Recording pipeline

I removed image encoding and I/O with disk from the camera acquisition callback
The resulting pipeline is:

camera acquisition thread
    → frame processing
    → handoff queue
    → handoff thread
    → shared-memory or standard thread queue on windows
    → image saver worker
    → TIFF file

Finally, each operation now gets each own worker abd runs independently and UI is scheduled with kivy Clock:

  • camera acquisition
  • image handoff and saving
  • tracking
  • live autofocus
  • plate scanning
  • multi-plate orchestration
  • go-to stage movement

@iliasoroka1
iliasoroka1 requested a review from takkasila July 27, 2026 12:51
@takkasila
takkasila requested a review from monikascholz July 27, 2026 14:35
@takkasila takkasila added feature New feature or request Medium Medium priority issue labels Jul 27, 2026

@takkasila takkasila left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not get to test all the functionality in time, so I trust that you have tested them throughly, especially the image saving and tracking. Apartfrom that I have mostly stylistic requests that I considerd good coding convention and highly recommend that you adopt them.

Important

  • Always write a clear class and function description. Along with type hints for arguments and return.
  • Write inline comments as much as possible to explain your intentions for the following code.

Good to have

  • Consistency between camel and underscore convention.
  • Add line space as often as you can. It improves reading comprehension.

It's a really big issue in a short time. You did a good job! The UI looks nice too!

Comment thread glowtracker/__main__.py

# Disable kivy console log
os.environ["KIVY_NO_CONSOLELOG"] = "1"
# os.environ["KIVY_NO_CONSOLELOG"] = "1"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should remain disabled.

Suggested change
# os.environ["KIVY_NO_CONSOLELOG"] = "1"
os.environ["KIVY_NO_CONSOLELOG"] = "1"

return not (y < y_lim and z > z_lim)

def _execute_safe_moves(self, target: List[float], cur: List[float], wait_until_idle: bool) -> None:
mm = units_from_literals('mm')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throughout this file, there are hardcoded unit = 'mm'. Please read them from config file or pass through function arguments.

Comment thread glowtracker/AutoFocus.py
Comment on lines +145 to +149
# def executePIDStep(self, image: np.ndarray, pos: float) -> float:
# """Perform one PID control step based on current image and lens position.

Returns:
relPosZ (float): estimated **relative** z-axis position to move to
"""
# Args:
# image (np.ndarray): gray-scaled image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this entirely.

import tifffile


def save_worker(image_queue, save_dir, filename_format, stop_event):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add argument type hints and function description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request Medium Medium priority issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants