Refactor pipeline to use grain crop classes#1022
Conversation
…ti class & subgrains
…mask required bool. Tested in debugger.
…2] >= 2, shape[1]==shape[2]
…rainCrops. Locally debugged working
|
Proposed solution to the data frame issue |
|
Its that or split into separate files. I'm ambivalent as to the preferred solution as I don't use the output but consideration for end users should be given. Whilst data management, manipulation, summarisation and plotting are, in my view, core skills for researchers these days experience levels vary widely and I don't know what would be easiest. |
|
Are we aiming to include this refactoring in |
Co-authored-by: Neil Shephard <n.shephard@sheffield.ac.uk>
… validation during construction using instance property 'padding'
- Spotted a few `print()` statements from debugging. - Explicitly test the number of grains below that are returned. - switching to a dictionary in the parameterisaion of `test_merge_classes()` instead of multiple individual options with comments/labels. The dictionaries are expressive about what the values are since the keys are the configuration options themselves. This in turn means we can just use `**vet_grains_conf` to unpack the dictionary of options when calling the `vet_grains()` function.
ns-rse
left a comment
There was a problem hiding this comment.
EPIC amount of excellent work @SylviaWhittle I think making our objects Classes is a really good decision long term and this gives us a good basis on which to build on.
I think you've seen #1092 which made some minor suggestions. I've looked through most things and think I have a general feel for things (many of the changes are names and/or where a functions options have changed removing those options).
Various in-line suggestions have been made.
tests/_regtest_outputs/test_grainstats_minicircle.test_grainstats_regression.out
Show resolved
Hide resolved
| grain_out_path_direction = grain_out_path / f"{direction}" | ||
| # Possibly delete this creation of the directory since we already do this earlier? | ||
| if plotting_config["image_set"] == "all": | ||
| grain_out_path_direction.mkdir(parents=True, exist_ok=True) |
There was a problem hiding this comment.
Not checked in detail but are we sure the grain_out_path_direction will exist and this line is no longer needed?
| ,image,grain_number,num_crossings,avg_crossing_confidence,min_crossing_confidence | ||
| grain_0,test_image,0,4,0.4013589828832889,0.2129989376767838 | ||
| grain_1,test_image,1,4,0.3441057054647598,0.17063184531586506 | ||
| grain_0,test_image,0,4,0.21426594097881008,0.001258249874731443 |
There was a problem hiding this comment.
Are we happy with these changes? They seem quite large.
There was a problem hiding this comment.
I was suspicious of these changes too, however I looked at the traces and the traces are fine, though they slightly different (due to randomness Max and I believe). the differences in the traces are not significant, but they do result in some quite different results as you can see. The crossings in question for that image are very difficult and a slight change in trace does produce a large change in results, however upon manual review, we believe these changes do reflect the slightly altered (but not problematic trace)
so TLDR: yes, caused by a small change in the trace due to randomness, turns out the tracing for that image is actually slightly better now (but that's just luck)
Co-authored-by: Neil Shephard <n.shephard@sheffield.ac.uk>
Co-authored-by: Neil Shephard <n.shephard@sheffield.ac.uk>
ns-rse
left a comment
There was a problem hiding this comment.
Looks great @SylviaWhittle
I think we will very soon have to deal with how these newly introduced classes are saved when we write .topostats files and loaded by AFMReader when we load them. That will no doubt be quite involved (we've both had a look through the nanosurf code and seen how they handle it!). I checked running against the latest AFMReader:main branch and lots of tests fail.
In light of that I think we will perhaps have to be careful in advising people to be wary of using any of the newly introduced entry points as they probably won't work, but we are also now in a catch-22 of TopoStats:main installing using the latest AFMReader on GitHub which as per above causes a lot of errors when loading .topostats objects. Hopefully not too many people are using the handful of entry points that have been added to the swiss-army knife. 🤞
|
|
||
| The U-Net model will take the bounding box of each grain, makes it square, and passees it to a trained U-Net model | ||
| which makes a prediction for a better mask, which then replaces the original mask. | ||
| Each `GrainCrop`'s image crop is passed to a trained U-Net model which makes a prediction for a better mask, which then |
There was a problem hiding this comment.
I've queried this before and think the answer was that the pre-trained U-net models can be provisioned on request.
Is that still the case?
If so do we have documented how to request the trained models, and once they have been received where to place them so they are loaded and used?
The topostats/default_config.yaml currently has unet_config["model_path"] defined as Null and the documentation states...
The path to the U-Net model to override traditional segmentation. Supply a path to a tensorflow U-net model to use, else U-Net segmentation will be skipped.
...but how are users (outside the Pyne Lab) to know where to get this from?
We should at least say something along the lines of...
Please contact topostats@sheffield.ac.uk for pre-trained models.
...at some point in the documentation.
NB This is something we should have had in place when the features were first merged into main, sorry for not picking up on this earlier. I'd be happy to make this a separate issue and deal with soon after this pull request is merged.
There was a problem hiding this comment.
I've added a little note explaining that our U-nets are available with our papers, but until the lab decide on how they want to (and make a list of papers) I'll write up an issue for this and leave it for now if that's okay?
There was a problem hiding this comment.
Thanks @SylviaWhittle
Saw the issue #1103 and put some thoughts in there. We'll just have to update these once the papers have been published and there is a reliable location to point people to for downloading.
| assert grainstats_df.shape[0] == 13 | ||
| assert len(grainstats_df.columns) == 22 | ||
| # Expect 6 grains in the above direction for cropped minicircle | ||
| assert grainstats_df.shape[0] == 6 |
There was a problem hiding this comment.
Presumably those small bits of dirt/noise get removed at some point for being below the minimal size threshold.
The refactoring to use classes for objects rather than dictionaries breaks the `topostats grainstats` entry point that was introduced with #1094. Previously the dictionary item `image=topostats_object["image"]` was passed into `processing.run_grainstats()` when called from `processing.process_grainstats()`. The refactoring requires that `image_grain_crop=image_grain_crop`, an object of the new type `ImageGrainCrops`, is now passed to `grainstats`. This isn't currently possible though because `AFMReader` loads `.topostats` objects and returns a dictionary and whilst the refactoring does save the new `image_grain_crop` (/`ImageGrainCrops`) the loading does _not_ currently re-create these structures. For now I have disabled the test of the entry point. Once this refactoring has been merged we will have to... - Make `TopoStats` a dependency of `AFMReader` (somewhat wary of this as it may cause headaches further down the line but for now we'll go with it!). - Modify `AFMReader.topostats.load_topostats()` to modify the `data["grain_tensors"]["above"]` and `data["grain_tensors"]["above"]` so that they are of class `ImageGrainCrops` (and the associated nested classes). - Once that is done we can then pass the loaded `data["grain_tensors"]` to `processing.run_grainstats()` (this may require reconstructing to be the same as `image_grain_crop`, not sure at the moment!)
…ntry-point-test tests: disable test_run_modules::test_grainstats


This PR is huge (sorry)Main things
This PR is designed to improve how we handle grains in the processing stages of TopoStats, starting at the grain finding stage, up to the disordered tracing stage. In future, this might be extended through the disordered tracing stage and beyond, however I've restricted the scope of this PR for the sake of everyone's sanity. The reason for stopping at disordered tracing is that once disordered tracing returns, all the data is wrapped up in neatly structured dictionaries, by grain and molecule, similar to what I've implemented, so I deemed this similar enough to not bother changing it yet.
The way this PR tries to standardize how we handle grains, is using DataClasses:
ImageGrainCropsaboveandbelow, each holding aDirectionGrainCropsobject for that direction's grain cropsGrainCropsDirectioncropsandfull_mask_tensorcropsstores dictionaries ofGrainCropobjects ([int, GrainCrop])full_mask_tensorstores a full sized mask for the image, size is NxNxC where C is the number of classes. This is NOT automatically updated when thecropsproperty is edited, this is because we don't want to update things during a loop. This can be discussed if this is an incorrect decision!GrainCropThis has the benefit of standardizing how we handle grains going forward, as we had previously been rather discordant in the types of data structures that we use in various parts of the codebase.
It also adds a helpful (I hope!!) layer of abstraction to processing functions, for example the
run_grainstatsfunction in processing no longer needs to takeimage,grain_masks,pixel_to_nm_scaling, it now takes justimage_grain_cropswhich contains all the data for each crop.This of course does come at the cost of increased memory usage as there are duplication of parts of images in the data structures as well as repeatedly listing the
pixel_to_nm_scalingfactor etc, however I personally find that the benefits here far outweigh the negatives. When working on the harbo-rings project, I found myself naturally extracting all the grains and storing them in a dictionary rather than keeping track of full image masks, I know Max also does this based on how he's handled the tracing code.disordered_tracing.pyprep_arrays. Prep arrays no longer needed, since it made a dictionary of grain crops, but we already have these now with the refactor.TopoStats Pull Requests
Please provide a descriptive summary of the changes your Pull Request introduces.
The Software Development section of
the Contributing Guidelines may be useful if you are unfamiliar with linting, pre-commit, docstrings and testing.
NB - This header should be replaced with the description but please complete the below checklist or a short
description of why a particular item is not relevant.
Before submitting a Pull Request please check the following.
docs/configuration.mddocs/usage.mddocs/data_dictionary.mddocs/advanced.mdand new pages it should link to.Optional
topostats/default_config.yamlIf adding options to
topostats/default_config.yamlplease ensure.topostats/validation.pyto ensure entries are valid.topostats/entry_point.py.