For the purpose of adding RADs to the frontend (#3), I think a refactor of the current codebase is in order. Below are a few suggestions, not to be treated as gospel.
A Plate class should be added encapsulating the plate with all its internal logic.
- It should be initialized with a plate image and appropriate configuration parameters (what is appropriate is to be determined on subsequent usage).
- As it's convenient to have an Image object open based on the image path, which requires closing, this class should also have a .close() method and conform to Python's 'with' protocol.
- The relevant functions at the module level will probably need to be refactored and folded into the class, instead of staying module-level.
The class will need to compute and/or output the following things:
- The disk location and size. This is already computed by
find_dda_disk.
- The radial pixel intensities (avg. pixel intensity vs. distance from the disk). The logic for this is implemented in
plot_pixel_intensities_from_plate:878-898. (But see NB 3 below!)
- The location (in pixels) of the RADs. This is implemented in
get_rads_from_pixels.
- Generating a copy of the image with the RADs marked on it. This is implemented by
draw_circles.
- The plot for the pixel intensities with the RADs. This is implemented in
plot_pixel_intensities and duplicated in plot_pixel_intensities_from_plate.
Regarding the panel creation, I recommend only a small refactor. Both functions should be able to take Image objects (as well as image paths). We can then pass them either the original image objects (if no RAD markings are requested) or copies with the RAD markings on them (see bullet number 4 in the previous paragraph), and no much needs to change. create_stacked_image now takes the rads parameter - this needs to be removed. Alternatively, you can pass them our new Plate objects directly, but it'll probably require more work.
NB: it is possible for some RADs to not exist. If the plate overgrew, there may not be 80% inhibition. In such case, the relevant field in the table should be empty. The plate may also have no growth, although in this case I think the RAD values will be defined, but meaningless.
NB 2: matplotlib takes a while to load in the packaged versions, and so we should only import it inside functions, not at the top of the file, globally.
NB 3: I think right now we're relying on the slice size to compute the RADs. If so, this is bad, becuase the slice size is just for display - the actual maximum intensity might be beyond the displayed slice. Thus, we need to use the whole plate to compute the RADs. But what is the plate size? In Phenobooth plates, the plate diameter is around 600-650 pixels. This is a reasonable default, but I'm not sure we should rely on this. I did write a find_petri_dish function in ddas.py, which we could perhaps use. However, it can give a larger diameter, since usually it will include a bit of the rim. As long as the rim is darker than the cells it shouldn't be a problem (since we compute the max intensity by the media of the top 50 or so pixels), but it might be.
NB 4: speaking of computing the max intensity, this is going to be a problem for very sensitive strains, as the region where they grow best might be smaller than 50 pixels in width, which will screw with our algorithm. I suppose we can reduce the width required, but that requires fine-tuning. diskImageR solved it by function-fitting, but I don't think we want to go down that route.
For the purpose of adding RADs to the frontend (#3), I think a refactor of the current codebase is in order. Below are a few suggestions, not to be treated as gospel.
A Plate class should be added encapsulating the plate with all its internal logic.
The class will need to compute and/or output the following things:
find_dda_disk.plot_pixel_intensities_from_plate:878-898. (But see NB 3 below!)get_rads_from_pixels.draw_circles.plot_pixel_intensitiesand duplicated inplot_pixel_intensities_from_plate.Regarding the panel creation, I recommend only a small refactor. Both functions should be able to take Image objects (as well as image paths). We can then pass them either the original image objects (if no RAD markings are requested) or copies with the RAD markings on them (see bullet number 4 in the previous paragraph), and no much needs to change.
create_stacked_imagenow takes theradsparameter - this needs to be removed. Alternatively, you can pass them our new Plate objects directly, but it'll probably require more work.NB: it is possible for some RADs to not exist. If the plate overgrew, there may not be 80% inhibition. In such case, the relevant field in the table should be empty. The plate may also have no growth, although in this case I think the RAD values will be defined, but meaningless.
NB 2: matplotlib takes a while to load in the packaged versions, and so we should only import it inside functions, not at the top of the file, globally.
NB 3: I think right now we're relying on the slice size to compute the RADs. If so, this is bad, becuase the slice size is just for display - the actual maximum intensity might be beyond the displayed slice. Thus, we need to use the whole plate to compute the RADs. But what is the plate size? In Phenobooth plates, the plate diameter is around 600-650 pixels. This is a reasonable default, but I'm not sure we should rely on this. I did write a
find_petri_dishfunction inddas.py, which we could perhaps use. However, it can give a larger diameter, since usually it will include a bit of the rim. As long as the rim is darker than the cells it shouldn't be a problem (since we compute the max intensity by the media of the top 50 or so pixels), but it might be.NB 4: speaking of computing the max intensity, this is going to be a problem for very sensitive strains, as the region where they grow best might be smaller than 50 pixels in width, which will screw with our algorithm. I suppose we can reduce the width required, but that requires fine-tuning. diskImageR solved it by function-fitting, but I don't think we want to go down that route.