Rectification with heading
Is your feature request related to a problem? Please describe. A vessel conducting a straight transect in open water may need to nose into the current in order to maintain positioning on the transect. This can result in scans which are not perpendicular to the transect. Since PM uses COG based on a smoothed trackline, the resulting pings are mislocated.
Describe the solution you'd like Option to georectify sonar mosaics from vessel heading rather then COG.
Describe alternatives you've considered NA
Additional context NA
TEST
Tried rectification with COG (left) and heading (right). Since pings are rectified from their waterfall version (essentially rubber sheeting), rectification with heading has minimal effect. The rectification needs to be done for each ping independently, then mosaiced into a final image.
Success!
But very slow... I have ideas.
Constructing sparse arrays may be the fastest. https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.coo_matrix.html
Implemented sparse arrays. Much faster. However, the NearestNDInterpolator is very slow. I did manage to get a mask using convex hull which works decent for masking interpolation output. Additionally, the whole process is very memory intensive. Likely need a numpy memory mapping approach.
Check out gdal.grid:
gdal.Grid(output_raster, point_shp, zfield="elevation",
outputSRS='EPSG:4326', # Replace with your desired CRS
format="GTiff", algorithm="invdist:power=2",
width=100, height=100) # Adjust dimensions as needed
https://gis.stackexchange.com/questions/254330/python-gdal-grid-correct-use
Masking interpolated grid can be done by:
- Fitting smoothed trackline
- Fitting smoothed range extent
- Create polygon from smoothed track and range lines
For memory issues, will need to write each chunks x, y coords to a file for each chunk.
- [x] The function that calculates each pings coords in parallel needs to process on a chunk basis, then iterate each chunk's pings.
Fixed the excessive memory usage issue with c1f9785d0e32039d28925680b6981a4407126969. It is not necessary to write any intermediate data files.
Tried gdal.Grid but the process was very slow. An implementation example is in c1f9785d0e32039d28925680b6981a4407126969 but has been commented out.
Interpolation continues to be the slowest part of the process..
Making progress:
On the inner bend, there appears to be 3 crab pots in the rubbersheeting approach where there should only be 1.
That is because during the range extent calculation during trackline smoothing, overlapping (crossing) pings are identified and reoriented so they do not cross.
https://github.com/CameronBodine/PINGMapper/blob/7a09faf4960b7053a6f9f3d359b7db74e2e42330/pingmapper/class_rectObj.py#L676-L702
This approach is intentional and helps fix rectification errors while rubbersheeting (image overlapping on itself) BUT has an impact on features imaged.
Rectification with heading is still slower than rubbersheeting. Things that help include:
- Decreasing the chunk size, thus decreasing number of pixels to interpolate for each chunk.
- Filtering out bends.
SRC
- [x] Added functionality to export slant range corrected imagery with da0b6cc6f121e35574e88e3239e5ba87f13655d6
Masking w/ Coverage
Trying to clip or mask the interpolated raster to the coverage of the pings has been difficult. The goal is essentially to constrain the interpolation between pings, but not beyond them, so that their aren't strange interpolation anomalies around the interpolation, as shown below:
The problem is particularly noticeable on the left which is the maximum range extent.
I have tried creating a mask using the trackline and the maximum ping extent. Essentially construct a polygon from the min and max coordinates for each ping. This approach was implemented in 6bdeeba72cb6f68c187fcac2bd13ebb94d0e1e13 but did not always work. Since the maximum range coordinate are not in "order" and cross each other, the resulting mask polygon can be misshapen, resulting in a mask, in this case, where the corner is chopped off:
Masking w/ Interpolation Distance Mask
It occurred to me that the distance mask used to mask the interpolation could be used, it just needed to be dilated. I did not try scipy.binary_erosion, but it may be viable. I did quickly find this solution which worked really well! Implemented in 79bfc11973518b793f97bb2f208469a92ab72346 resulting in:
There may be a tiny bit of edge effect on the left, but negligible. But along the track and the top and bottom are perfect! Doesn't seem to slow either. That's a win!
Now to test on the terrible recording...
Drumroll please...! And:
whelp...
Examining the mosaic, there are strange across track artifacts. It looks like pixel coordinates are not aligning.
There is some sort of precision error when calculating coordinates. It could stem from a few sources:
- Range calculations
- Heading precision
- Projecting Humminbird coords to UTM
- others?
Speed corrected sonogram for reference:
Smoothing the heading made a huge difference (f1b9d59f9dbbede266e829b80f2cd46798236bf8)
Settled on the best results after adjusting earth radius to match wgs 1984 and making sure pixel coordinates are rounded rather then directly converted to int. Rubber sheeting on left and heading on right:
Linear interpolator with griddata is faster and produces better results.
The remaining noise that was present in the NDNeighbors approach have been reconciled with this approach. Nearest Neighbors was producing many of the artifacts. Likely IDW would not have produces this issue.
Still need to work out issues with heading smoothing.