rplatypus rebuilt: deep learning for medical images in R

Wait 5 sec.

[This article was first published on Data Science Guts, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)Want to share your content on R-bloggers? click here if you have a blog, or here if you don't. In October 2020 I wrote about platypus, an R package for object detection and image segmentation: YOLOv3 and U-Net in a few lines of R, on top of the keras and tensorflow packages. People used it. It collected 134 stars and a queue of issues – and then it quietly stopped working, and I stopped answering them.This year I went back to it. Computer vision is the part of data science I enjoy most, and this package has always been my passion project – the place where I get to learn things my day job does not ask of me. What came out is not an update but a different package, with a new name, a different backend and a narrower purpose: rplatypus, deep learning for medical images in R. It has been submitted to CRAN and is waiting for review; until it is accepted, install it from GitHub:# install.packages("remotes")remotes::install_github("maju116/rplatypus")This post is about why it had to be rewritten rather than fixed, what is in it now, and where the series of posts that follows is going.Why the old one brokeR’s keras and tensorflow packages are not bindings to a C++ library. They are bridges, through reticulate, into a Python installation of TensorFlow – and in 2020 that was the Python installation, the one shared with everything else on the machine. Upgrade numpy for an unrelated project and an R session using platypus could stop loading. The failure never appeared in my package’s code, which made it miserable to support: the bug reports were about other people’s Python.The second problem is the one that actually made me stop. platypus sat on two layers I did not control: the R keras and tensorflow packages on top of TensorFlow itself. When TensorFlow changed in Python, the R packages had to catch up before platypus could, and that often took a very long time. Until they did, my package was stuck, with issues I could not fix from my side. After enough of that, I lost the will to keep working on it.Two things have changed since, and together they remove both problems at the root rather than patching around them.reticulate learned to isolate. Since version 1.41, a package declares its Python requirements with py_require(), and reticulate builds a private environment for them with uv, cached and shared with nobody. There is no common Python left for two packages to fight over. rplatypus never touches a Python anyone else uses – and the person installing it never chooses an interpreter, activates anything or matches a version. The first call that needs the engine builds the environment; every call after that reuses it. And there is no R wrapper in between any more: rplatypus calls its own engine, which I release myself, so nothing waits for anybody else to catch up.The medical imaging world settled on PyTorch. MONAI, nnU-Net, TorchIO, timm, the pretrained encoders and the foundation models – all of it is PyTorch. TensorFlow for medical images is effectively over, and the TensorFlow 2.9 the old engine was pinned to does not even install on Python 3.12. Staying would have meant rewriting anyway, onto a backend with no future in the field.A third thing changed too. It does not fix the old failure, but it is the reason the rewrite happened at all instead of staying a good idea: writing a package got much cheaper. In 2020 I wrote every line of platypus myself, and the parts that make a package trustworthy – tests, documentation, error messages that say what went wrong – were the parts I never had time left for. This time I worked with an AI coding agent, Claude Code.I want to be precise about what that means, because “AI wrote my package” is not it. The decisions were mine: what the package is for, which datasets to measure it on, what it should refuse, and what a result has to show before it goes into the documentation – every number in this series was measured, not generated. What the agent changed was the cost of everything around those decisions: writing the tests, keeping two packages in two languages in step, chasing a failing check through reticulate, uv and CUDA. That is exactly the work that used to end side projects like this one. The rewrite took around a month.So the computation moved to pyplatypus, a Python engine on PyTorch, and rplatypus is the R package on top of it. The split is deliberate:The engine is in Python, because that is where the models, the weights and the medical libraries already are. One codebase to maintain, and everything PyTorch has is within reach.The product is in R, because that is where the gap is. Reading and combining masks, plotting, comparing models, the analysis around the network – that part is plain R and needs no Python at all.I did consider native R torch, which binds libtorch directly and needs no Python. For one person maintaining this in their evenings it would have meant writing every architecture, loss and data pipeline twice, and giving up every pretrained weight in the Python ecosystem.pyplatypus: the same engine, from PythonThe engine is not hidden plumbing. pyplatypus is a package on PyPI in its own right – it too started life in 2022 on TensorFlow and was rewritten alongside – and if you work in Python you can use it directly:pip install "pyplatypus[hub]"The example below runs from both languages, out of one file.The two are developed together, and in a fixed order. A feature lands in the engine first, the engine is published to PyPI, and only then does rplatypus move to it – each release of the R package pins one exact version of pyplatypus, so it cannot break because the engine changed underneath it. They also share one configuration format: the same YAML file describes the same experiment whichever language reads it, so an R user and a Python user can hand each other a run.That is also how this series is written. The examples are in R, because that is where the gap is, and each post shows what the same step looks like from Python.The gapHere is the part that convinced me this was worth doing. R has excellent tools for reading medical images – RNifti, oro.dicom, divest – and a healthy deep learning stack. Almost nothing joins them. When I checked in September, the CRAN Task View for Medical Image Analysis (last updated August 2025) listed no deep learning packages at all.So rplatypus is meant to be that missing piece: not a wrapper around a Python library you then have to learn, but an R package that happens to compute in Python.A mask, without training anythingThe quickest way to see whether a package does anything useful is to borrow a model someone already trained. rplatypus publishes weights by name – here a U-Net trained on the 2018 Data Science Bowl, the same nuclei dataset I used on this blog in 2020.The folder nuclei/ holds four microscopy images in the dataset’s own layout – one directory per image, with images/ and masks/ inside.library(rplatypus)spec