Perpendicular Banding - could be mechanical, could be artifact
Since the latest 3.0.x versions, I noticed that banding, perpendicular to the scanning direction, shows up on the output images.
While I cannot rule out mechanical issues, this is something quite new to me. I will run the same file through an ancient JSolEx installation, to see where it goes.
These two are adjacent pixel shifts as shown by their filenames.
exagerrated in imagemath
It may be a
- bad / vibrating recording, or
- a camera readout issue
I tested with 2.11.3 dev version, and the same thing shows up
mmm, the edges are suffering from extreme jagging, this is quite surprising. Either you have terrible seeing, or it may be vibrations indeed.
seeingwise, this is actually .... good, given my location :D
The bands seem to match the most shifted lines, so I wonder if it could be an artifact of banding correction, if the band size is too small?
the pattern is parallel to the scanning slit, that's why I think it could be either a camera readout thing or a mechanical vibration of perhaps one or two pixels
I put in some more screws and metal plates to consolidate the structure, hopefully it is a mechanical issue, and a shaking (by two-three microns) of the camera was the culprit.
this now is extra good seeing, by the ersatz-obsi's measure, btw -- my obsi's location sux
I inspected videos more closely today, visually scrolling through AS4, with an anchor point dropped onto a thin spectral line, looking for vertical wobble. I saw nothing obvious.
I noticed that the pixel shift that you are using when these bands are visible is particularly high (-220). I wonder if this could be related to the polynomial interpolation, if the line observed is particularly thin... Not sure, but I'm running out of ideas.
It happens at -72 also. Here I was trying to dig out He I details, 447.15 nm
Let me upload the video and script. Visually, scrolling through the ser video, it kind of looks OK, all the vertical stripes make it hard to notice anything funny, but the X stays on band
Helium447p15M72_offset = -72
Helium447p15M72_context = 0
Helium447p15M72_step = 1
kontinuum_offset = -54
kontinuum_context = 3
kontinuum_step = 1
kontL = kontinuum_offset - kontinuum_context
kontR = kontinuum_offset + kontinuum_context
Helium447p15M72L = Helium447p15M72_offset - Helium447p15M72_context
Helium447p15M72R = Helium447p15M72_offset + Helium447p15M72_context
lineZero = min(autocrop2(range(-1, 1)))
lineZeroB = fix_banding(lineZero, 100, 50)
lineZeroBL = linear_stretch(lineZeroB)
lineZeroL = linear_stretch(lineZero)
lineZeroBLSQ = linear_stretch(pow(lineZeroB, 2))
[outputs]
Helium447p15M72Raw = min(autocrop2(range(Helium447p15M72L,Helium447p15M72R,Helium447p15M72_step)))
Helium447p15M72RawRot = rotate_rad(linear_stretch(Helium447p15M72Raw), angleP)
kontinuum = avg(autocrop2(range(kontL,kontR,kontinuum_step)))
kontinuumLinearRotated = rotate_rad(linear_stretch(kontinuum), angleP)
Helium447p15M72_delta2 = fix_banding(Helium447p15M72Raw - kontinuum, 100, 50)
Helium447p15M72Log = linear_stretch(log(Helium447p15M72Raw, 2))
Helium447p15M72LogRot = rotate_rad(Helium447p15M72Log, angleP)
Helium447p15M72LogHelium447p15M72Raw = linear_stretch(pow(Helium447p15M72Log + Helium447p15M72Raw, 1.2))
Helium447p15M72Mul = linear_stretch(Helium447p15M72LogHelium447p15M72Raw * Helium447p15M72_delta2)
Helium447p15M72Min = min(Helium447p15M72LogHelium447p15M72Raw, Helium447p15M72Mul)
Helium447p15M72Prime = linear_stretch(Helium447p15M72Log + Helium447p15M72Min + linear_stretch(pow(Helium447p15M72Min,4)) + linear_stretch(pow(Helium447p15M72Raw,2)) + Helium447p15M72LogHelium447p15M72Raw)
Helium447p15M72PrimeRot = rotate_rad(Helium447p15M72Prime, angleP)
Helium447p15M72PrimeRotP = linear_stretch(pow(Helium447p15M72PrimeRot, 1.1))
Helium447p15M72PrimeRotFinalPow1 = linear_stretch(avg(Helium447p15M72PrimeRotP, Helium447p15M72PrimeRot))
Helium447p15M72PrimeRotFinalPow2 = linear_stretch(pow(avg(Helium447p15M72PrimeRotP, Helium447p15M72PrimeRot), 2))
Helium447p15M72PrimeRotFinalPow3 = linear_stretch(pow(avg(Helium447p15M72PrimeRotP, Helium447p15M72PrimeRot), 3))
Helium447p15M72PrimeRotFinalPow3PlusLog = linear_stretch(Helium447p15M72LogRot + Helium447p15M72PrimeRotFinalPow3)
Helium447p15M72PrimeRFP3PlusLog = max(Helium447p15M72PrimeRotFinalPow1, Helium447p15M72PrimeRotFinalPow3PlusLog)
Helium447p15M72PrimeRFP3PlusLogSq = linear_stretch(pow(Helium447p15M72PrimeRFP3PlusLog, 1.4))
lineZeroLRot = rotate_rad(lineZeroL, angleP)
lineZeroBLRot = rotate_rad(lineZeroBL, angleP)
lineZeroBLRotSQ = rotate_rad(lineZeroBLSQ, angleP)
Helium447p15M72_delta2Rot = rotate_rad(linear_stretch(Helium447p15M72_delta2), angleP)
Helium447p15M72MulRot = rotate_rad(Helium447p15M72Mul, angleP)
Helium447p15M72MulNRot = linear_stretch(Helium447p15M72RawRot * Helium447p15M72MulRot)
Helium447p15M72PrimeRFP3PlusLogSqPlusHelium447p15M72MulNRot = linear_stretch(Helium447p15M72RawRot * Helium447p15M72MulRot + Helium447p15M72PrimeRFP3PlusLogSq)
the video is (uploading) at http://narnia.go.ro/seagate2tb.php?mask=2025-04-16
Present on the tilt image, so at the moment I have three hypotheses
- some camera readout issue
- pixel level vibrations shifting the spectrum red-blue
- some jsolex artefact
INTI usually fails at obscure lines and such, so even though I usually use it to sanity check, sometimes it's just not worth the effort.
at 300% there is maybe some wobble in the spectral line's position, in this particular video. If the clouds are nice, I'll see how it fares with the screws, an ND filter to keep the redout at around 1ms and not below, and see what happens. Maybe it is that simple, it happened to vibrate cause I somehow hit the right length for something to resonate, idk
That's an option indeed. You're also using a cooled camera right? If so maybe the fan is causing enough vibration to mess things up.
Everything trivial is accounted for:
- camera and RA-mount-motor fans stop on scan start (and restart on scan finish)
- there is ramping to minimize vibrations
- cables are arranged so they don't pull on anything, not even with there own weight (anchor points)
- declination backlash is sort-of accounted for, as in tension is left in the gears, hoping it wouldn't dance around in the backlash related no man's land, but that is related to the jagged edges, not the spectrum moving around
I mean as shitty as my location and setup are, I tried to bring out the best of this shitty situation called life.
I tried the video debugger in the 3.0.2 version, a per frame calculation, and it indeed shows a one pixel shift (the d in the ax3 + bx2 + cx + d poly if I get it correctly), around 120-122 for the outer half of the disk, and 121 for the disk center -- there is a reason the average is used.
The pattern is too uniform to not have come from a resonance, either mechanical or digital, but perhaps too uniform to be purely mechanical.
It's just a shame that the otherwise decent seeing didn't yield the nice pictures I was hoping for.
I have a suitcase full of SSDs, so for now I kind of have the storage space to put the raws aside and tinker with them later.
that's a He I 447.15 filament btw
I did the following:
- spacially trimmed the ser video in PIPP to exclude the dark spectral line at the bottom
- I let JSolex video debugger to crunch the video to obtain a forced poly
- I set new values for the helium I was looking for -- same continuum region, same helium line, just approached from closer and from the top
- I got this image, way less banding
- This doesn't rule out the vibrations, maybe half a pixel makes the difference, but point to a software artefact
Helium447p15M73_offset = 33
Helium447p15M73_context = 0
Helium447p15M73_step = 1
kontinuum_offset = 46
kontinuum_context = 5
kontinuum_step = 1
kontL = kontinuum_offset - kontinuum_context
kontR = kontinuum_offset + kontinuum_context
Helium447p15M73L = Helium447p15M73_offset - Helium447p15M73_context
Helium447p15M73R = Helium447p15M73_offset + Helium447p15M73_context
lineZero = min(autocrop2(range(-1, 1)))
lineZeroB = fix_banding(lineZero, 100, 50)
lineZeroBL = linear_stretch(lineZeroB)
lineZeroL = linear_stretch(lineZero)
lineZeroBLSQ = linear_stretch(pow(lineZeroB, 2))
[outputs]
Helium447p15M73Raw = min(autocrop2(range(Helium447p15M73L,Helium447p15M73R,Helium447p15M73_step)))
Helium447p15M73RawRot = rotate_rad(linear_stretch(Helium447p15M73Raw), angleP)
kontinuum = avg(autocrop2(range(kontL,kontR,kontinuum_step)))
kontinuumLinearRotated = rotate_rad(linear_stretch(kontinuum), angleP)
Helium447p15M73_delta2 = fix_banding(Helium447p15M73Raw - kontinuum, 100, 50)
Helium447p15M73Log = linear_stretch(log(Helium447p15M73Raw, 2))
Helium447p15M73LogRot = rotate_rad(Helium447p15M73Log, angleP)
Helium447p15M73LogHelium447p15M73Raw = linear_stretch(pow(Helium447p15M73Log + Helium447p15M73Raw, 1.2))
Helium447p15M73Mul = linear_stretch(Helium447p15M73LogHelium447p15M73Raw * Helium447p15M73_delta2)
Helium447p15M73Min = min(Helium447p15M73LogHelium447p15M73Raw, Helium447p15M73Mul)
Helium447p15M73Prime = linear_stretch(Helium447p15M73Log + Helium447p15M73Min + linear_stretch(pow(Helium447p15M73Min,4)) + linear_stretch(pow(Helium447p15M73Raw,2)) + Helium447p15M73LogHelium447p15M73Raw)
Helium447p15M73PrimeRot = rotate_rad(Helium447p15M73Prime, angleP)
Helium447p15M73PrimeRotP = linear_stretch(pow(Helium447p15M73PrimeRot, 1.1))
Helium447p15M73PrimeRotFinalPow1 = linear_stretch(avg(Helium447p15M73PrimeRotP, Helium447p15M73PrimeRot))
Helium447p15M73PrimeRotFinalPow2 = linear_stretch(pow(avg(Helium447p15M73PrimeRotP, Helium447p15M73PrimeRot), 2))
Helium447p15M73PrimeRotFinalPow3 = linear_stretch(pow(avg(Helium447p15M73PrimeRotP, Helium447p15M73PrimeRot), 3))
Helium447p15M73PrimeRotFinalPow3PlusLog = linear_stretch(Helium447p15M73LogRot + Helium447p15M73PrimeRotFinalPow3)
Helium447p15M73PrimeRFP3PlusLog = max(Helium447p15M73PrimeRotFinalPow1, Helium447p15M73PrimeRotFinalPow3PlusLog)
Helium447p15M73PrimeRFP3PlusLogSq = linear_stretch(pow(Helium447p15M73PrimeRFP3PlusLog, 1.4))
lineZeroLRot = rotate_rad(lineZeroL, angleP)
lineZeroBLRot = rotate_rad(lineZeroBL, angleP)
lineZeroBLRotSQ = rotate_rad(lineZeroBLSQ, angleP)
Helium447p15M73_delta2Rot = rotate_rad(linear_stretch(Helium447p15M73_delta2), angleP)
Helium447p15M73MulRot = rotate_rad(Helium447p15M73Mul, angleP)
Helium447p15M73MulNRot = linear_stretch(Helium447p15M73RawRot * Helium447p15M73MulRot)
Helium447p15M73PrimeRFP3PlusLogSqPlusHelium447p15M73MulNRot = linear_stretch(Helium447p15M73RawRot * Helium447p15M73MulRot + Helium447p15M73PrimeRFP3PlusLogSq)
Makes sense. I think reusing the same polynomial for a line which is far away isn't a good idea. Line detection is itself extremely tricky (you wouldn't believe how much time I spend on tweaking this). Cropping the SER file on the region of interest is reasonable in this case.
I agree with you about cropping to a ROI.
However, what a far away line should do is deviate and go off band, not oscillate.
I don't think it oscillates. I think it's an artifact of interpolation (discrete pixels) and rounding which will not work well if the polynomial is not as close as possible to the "real" line.
I noticed something in the raw camera output, when zoomed in and at high gain with very low expo time
I think it is a bias issue, and the perpendicular lines appear when the spectral line, as it curves, passes through the horizontal scanlines affected by this
Oh, that's an interesting finding. Which camera is this?
ZWO ASI 678MM, with external peltier cooling, sensor kept at 20C
I managed to mitigate the issue by increasing the exposure time, and that by putting a neutral density filter into the light train. At around 1-1.5 ms expo time, the camera performs decently and the stripes are mostly gone