Every account of an end-to-end detection model starts at the architecture and ends at the metric. The part in the middle, where someone has to produce the labels the model regresses against, gets one line in the methods section and never comes up again. On this engagement, a roughly twenty-month project with a mid-sized Middle East carbonate operator, that middle part was a single notebook cell, and it is worth writing down exactly what it did, because it is the join between two worlds that otherwise never touch: a geologist reading waves off a borehole image by eye, and a transformer that has never seen a geologist.
The labels we needed did not exist as pixels. They existed as a spreadsheet. The operator's interpreters had already done the interpretation, pick by pick, and handed us the result as a Feature Listing export: one row per identified feature, with columns for measured depth (MD), true dip (Dip_TRU), azimuth (Azimuth), and a type flag that read B for a bedding plane. That is a clean, human-readable table. It is also useless to a detection network, which does not consume table rows. It consumes an image and emits geometry. So the whole job of this step was to turn each row back into the shape the interpreter saw when they made the pick in the first place: a sinusoid on the unrolled borehole wall.
The one line that does the work
A tilted plane cutting a cylinder unrolls into a sine wave. That is not an approximation, it is geometry, and it is why every planar feature in a borehole image is a wave rather than a straight line. The interpreter had collapsed each wave down to three numbers when they logged the pick. To make a label, we had to run that collapse backwards and regenerate the wave the numbers imply.
The notebook did it with one expression, looped over every angle around the borehole:
for the swept angle theta, with dip and azimuth in degrees. Read it slowly. tan(Dip) sets the half-height of the wave, so a steep pick makes a tall sinusoid and a gentle one makes a shallow ripple. Azimuth sets the horizontal phase, so the wave reaches its trough at the compass direction the plane dips toward. And the MD from the same row sets where the whole curve sits vertically, its offset down the well. Three input numbers, one regenerated curve, drawn at its measured depth. Do that for every row and the spreadsheet becomes a picture of stacked waves, which is a picture of labels.
Watching a row become a curve
The forge below is that cell, made interactive. It takes one Feature Listing row, sweeps theta from 0 to 359 the way the notebook loops, and traces the sinusoid the row implies. On the right it does the same for a ladder of picks stacked down-depth, which is the synthetic labelled section the detector was trained against.
The point the exhibit argues is deliberately unimpressive. There is no learning here, no clever loss, no model at all. There is a spreadsheet, a trig identity, and a loop. That is the entire bridge from human interpretation to machine-readable ground truth, and it is the reason a set-prediction transformer could later emit (depth, dip, azimuth) per feature instead of painting pixel masks and fitting curves as an afterthought. The label schema and the detector's output head are the same three numbers, because both are downstream of this one regeneration.
What one well actually gave us
The notebook this cell came from was named for the well it ran on, and that well's Feature Listing had 1502 rows. Subtract the header and you get 1501 valid picks, every one of them Type B, spanning a measured-depth range of 2227.9 to 2890.3 m. So a single well, interpreted by hand and exported to Excel, yielded 1501 bedding labels for the cost of running one loop. The interpretation work was already paid for by the operator, and the plumbing recovered it as supervised training signal for free.
An earlier version of the same notebook, from late January 2022, is a good reminder that the plumbing was not born clean. It read a file whose name claimed a 1640 to 1700 m interval, while the data inside actually spanned 2640.13 to 2699.84 m. The filename was simply wrong, mislabelled by someone upstream, and the code trusted the data over the name. That version read 534 rows and kept 532 usable picks, carrying lithology tags of Dol, Clay, and Bb alongside the dip and azimuth. Small numbers, but they are the primordial form of the same idea, running months before the supervised pivot the whole project later turned on.
The labels are honest about being sparse
There is a temptation to treat these regenerated sinusoids as dense, complete truth. They are not, and the pipeline had to know it. A geologist picks the features that matter to them, not every plane that exists, and they pick unevenly down the well. When we plotted the distance between one pick and the next across a whole well, most spacings sat near zero, features clustered tight where the rock was interesting, but the gaps ran out to roughly 28 m where nothing was logged. A 28 m stretch with no picks does not mean 28 m with no bedding. It means 28 m no one wrote down.
That single fact reshaped the evaluation downstream. If the ground truth is known to be incomplete, then a detection the model makes in an unlabelled gap cannot be scored as a hard false positive, because there may well be a real feature there that no human recorded. The sparse, irregular labelling the forge is built from is exactly why the later evaluation had to tolerate unmatched predictions rather than punish them. The plumbing and the metric were designed against the same limitation.
Why this deserves more than one line
It is easy to skip this step in the write-up because it contains no research. But the quality of everything after it is bounded by what happens here. If the regeneration form is wrong, the labels are wrong, and no architecture recovers from wrong labels. If the depth offset is misread, every curve sits at the wrong place and the model learns a shifted world. If the apparent dip and azimuth are fed in as though they were true, the labels are quietly rotated off the earth frame. This cell is small, but it is load-bearing.
So the join between the geologist and the transformer turned out to be one expression, one loop, and a spreadsheet the operator had already filled in. We regenerated 1501 interpreted picks into 1501 sinusoids, stacked them at their measured depths, and handed the result to a network as ground truth. There was nothing sophisticated about it, and that is the point worth recording: the hardest-earned interpretation in the whole subsurface workflow reaches the model through the plainest code in the pipeline.
Limitations
The single demonstration row in the exhibit, its dip of 34 degrees, azimuth of 118 degrees, and MD of 2440.0 m, is one representative bedding pick chosen inside the sourced 2227.9 to 2890.3 m range for legibility; the reconstruction form and the depth ranges are sourced from the notebooks, but that individual row's three values are illustrative. The stacked ladder on the right samples depths evenly across the sourced range to read like a real section; the true pick spacing is uneven, as the caveat strip records. The dip and azimuth handled here are apparent values as logged; converting them to true earth-frame values requires the tool-angle and well-deviation channels and is a separate step not shown in this cell. Finally, the pick counts and depth ranges describe the specific wells these two notebooks ran on and are not representative of the full multi-well dataset the final models were trained on.