• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pinneylab / htbam_analysis / 15034886290

15 May 2025 01:54AM UTC coverage: 19.059%. First build
15034886290

push

github

web-flow
Merge pull request #29 from pinneylab/binding-processing-refactor

Binding processing refactor

31 of 65 new or added lines in 2 files covered. (47.69%)

470 of 2466 relevant lines covered (19.06%)

0.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/htbam_analysis/scripts/corner_picker.py
1
import argparse
×
2
from bokeh.models import ColumnDataSource
×
3
from bokeh.plotting import figure
×
4
from bokeh.layouts import gridplot, column
×
5
import numpy as np
×
6
from PIL import Image
×
7

8
from bokeh.io import curdoc
×
9
from bokeh.events import Tap
×
10
from bokeh.models import Slider
×
11
from bokeh.models import Div
×
12

13
"""
14

15
"""
16

17
def compute_corner_slices(stitched_image):
×
18
    
19
    fraction = 1/8
×
20
    height, width = stitched_image.shape
×
21
    h_crop = int(height * fraction)
×
22
    w_crop = int(width * fraction)
×
23

24
    slices = [
×
25
        (slice(0, h_crop), slice(0, w_crop)),                            # Top-left
26
        (slice(0, h_crop), slice(width - w_crop, width)),               # Top-right
27
        (slice(height - h_crop, height), slice(0, w_crop)),            # Bottom-left
28
        (slice(height - h_crop, height), slice(width - w_crop, width)) # Bottom-right
29
    ]
30

31
    return slices
×
32

33
# init parser
34
parser = argparse.ArgumentParser(description='Launches corner-picking GUI')
×
35
parser.add_argument("stitched_image", type=str, help='Path to a stitched image.')
×
36
args = parser.parse_args()
×
37
stitched_image = np.array(Image.open(args.stitched_image)).astype(float)
×
38

39
# grab corners
40
corners = ['Top-Left', 'Top-Right', 'Bottom-Left', 'Bottom-Right']
×
41
corner_slices = compute_corner_slices(stitched_image)
×
42

43
plots = []
×
44
click_sources = []
×
45
corner_coords = [None] * 4
×
46
coord_display = Div(text="COPY/PASTE ME INTO PROCESSING NOTEBOOK: (None, None), (None, None), (None, None), (None, None)", styles={"font-size": "20px"})
×
47

48
image_sources = []  # To store the original cropped images
×
49
image_renderers = []  # To update image brightness later
×
50

51
for idx, s in enumerate(corner_slices):
×
52

53
    # Load and convert to NumPy
54
    corner_image = stitched_image[s]
×
55
    height, width = corner_image.shape
×
56
    x0, y0 = s[0].start, s[1].start
×
57
    dw, dh = s[1].stop - s[1].start, s[0].stop - s[0].start
×
58

59
    # Click source for recording clicks
60
    source = ColumnDataSource(data=dict(x=[], y=[], index=[]))
×
61
    click_sources.append(source)
×
62

63
    # Set up plot
64
    # TODO: make width and height more flexible
65
    p = figure(
×
66
        title=corners[idx],
67
        width=800, height=800,
68
        tools="tap",
69
        x_range=(s[1].start, s[1].stop),
70
        y_range=(s[0].stop, s[0].start)
71
    )
72

73
    # Store for brightness adjustments
74
    img_cropped_original = corner_image.copy()
×
75
    image_sources.append(img_cropped_original)
×
76

77
    renderer = p.image(image=[corner_image], x=y0, y=x0, dw=dw, dh=dh, palette="Greys256")
×
78
    image_renderers.append(renderer)
×
79

80
    p.scatter('x', 'y', size=5, color='red', alpha=0.6, source=source)
×
81
    
82
    def make_callback(i, src):
×
83
        def on_tap(event):
×
84
            # Save coordinate
85
            src.data = dict(x=[event.x], y=[event.y])
×
86

87
            y_offset = 0
×
88
            x_offset = 0
×
89
            corner_coords[i] = (event.x + x_offset, event.y + y_offset)
×
90

NEW
91
            formatted = "COPY/PASTE ME INTO PROCESSING NOTEBOOK: " + ", ".join(
×
92
                f"(({f'{pt[0]:.0f}' if pt is not None else 'None'}, {f'{pt[1]:.0f}' if pt is not None else 'None'}))"
93
                for pt in corner_coords
94
            )
95

96
            coord_display.text = formatted
×
97
        return on_tap
×
98

99
    p.on_event(Tap, make_callback(idx, source))
×
100

101
    plots.append(p)
×
102

103
def update_brightness(attr, old, new):
×
104
    scale = brightness_slider.value
×
105
    for i in range(4):
×
106
        tmp = image_sources[i].copy()
×
107
        adjusted = np.clip(tmp * scale, 0, 255)
×
108
        image_renderers[i].data_source.data = dict(
×
109
            image=[adjusted],
110
            x=[image_renderers[i].glyph.x],
111
            y=[image_renderers[i].glyph.y],
112
            dw=[image_renderers[i].glyph.dw],
113
            dh=[image_renderers[i].glyph.dh]
114
        )
115

116
brightness_slider = Slider(start=0.1, end=5.0, value=1.0, step=0.1, title="Brightness", width=800, styles={"font-size": "20px"})
×
117
brightness_slider.on_change("value", update_brightness)
×
118

119
# Arrange in a 2x2 grid
120
grid = gridplot([[plots[0], plots[1]], [plots[2], plots[3]]])
×
121
curdoc().add_root(column(grid, brightness_slider, coord_display))
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc