Machine learning inside the microscope: Nikon’s NIS.ai and the open tools behind it

This started life in 2020 as a one-line note pointing at a page on Nikon’s website. The page is still worth pointing at, because it is a clear example of something that has quietly happened across microscopy: the machine learning is now inside the microscope software, sold as a set of modules, and a great many neuroscience images are passing through convolutional networks before anyone looks at them. This is a short guide to what those modules do, what they are under the hood, which free tools do the same job, and the one thing you must check before trusting any of them. Updated October 2026.

What Nikon ships

NIS.ai is a family of add-ons for NIS-Elements, the acquisition and analysis software that drives Nikon microscopes. Each module is a neural network that takes an image in and gives an image, or a set of labelled regions, back. Some come pre-trained; the more interesting ones learn from your own data. Nikon’s page is the authority on current pricing and bundling, which I will not repeat here because it changes; at the time of writing Denoise.ai ships with the AR package and the rest are licensed separately, and all of them want a GPU.

NIS.ai moduleWhat it doesTrained onThe open-source equivalent
Denoise.aiRemoves shot noise from confocal imagesPre-trained by NikonNoise2Void, CARE
Clarify.aiRemoves out-of-focus blur from widefield fluorescencePre-trained by NikonDeconvolution; CARE
Enhance.aiRestores detail in under-exposed imagesYour own paired imagesCARE
Convert.aiPredicts one channel from another, e.g. a nuclear stain from brightfieldYour own paired imagesIn-silico labelling; CARE’s image-to-image mode
Segment.aiFinds structures that thresholding cannot, from hand-traced examplesYour own traced imagesCellpose, StarDist, ilastik

Two of these are the kind of thing you would expect a camera company to ship. Denoising and deblurring have a long history in microscopy, deconvolution in particular, and a network trained on a lot of Nikon images is a sensible modern replacement. The other three are more surprising, and more important.

What they are, underneath

Every NIS.ai module is a descendant of a handful of academic papers from 2015 to 2021, all of them open, most of them with code. Knowing the lineage tells you what the tool can and cannot do.

  • U-Net (Ronneberger, Fischer and Brox, 2015). A convolutional network shaped like a U: it shrinks the image to understand it, then grows it back to the original size to label every pixel. Written for biomedical segmentation, and still the backbone of nearly every image-to-image tool below, including, almost certainly, Nikon’s.
  • CARE, content-aware image restoration (Weigert and colleagues, 2018). Train a U-Net on pairs of bad and good images of the same sample (low light and high light, low resolution and high resolution) and it learns to turn bad into good. This is Enhance.ai’s recipe, and the CSBDeep package is the open version.
  • Noise2Void (Krull, Buchholz and Jug, 2019). Denoising without clean examples: the network learns to predict each pixel from its neighbours, which it can only do for the signal and not for the noise. Pre-trained denoisers like Denoise.ai are a cousin of this idea.
  • In-silico labelling (Christiansen and colleagues, 2018; Ounkomol and colleagues, 2018). Predict a fluorescent stain from a transmitted-light image, because the structure the stain reveals is faintly present in the unstained picture. This is Convert.ai, and it is the module most likely to make a biologist gasp and a statistician wince, for the same reason.
  • Cellpose (Stringer, Wang, Michaelos and Pachitariu, 2021) and StarDist. General-purpose cell and nucleus segmentation that works out of the box on most images and can be fine-tuned on yours in a GUI. Segment.ai does the same job inside NIS-Elements. Cellpose comes from the Janelia group whose data appears in the data page, and it is the tool I would reach for first.

The thing to check

An image-to-image network produces a plausible image. That is its job, and it is also the problem. A restored or converted image can contain structure that was never in the sample: a denoiser that has seen a lot of mitochondria will draw mitochondria into noise, and a channel-prediction network will confidently paint a nucleus where the training set says one usually is. Nothing in the output flags this. The people who wrote CARE say so plainly in the paper, and the rule they give is the right one:

  • Keep the raw images, always, and do your quantification on them or on something you can trace back to them.
  • Validate on data where you know the answer: hold out real pairs, as in lesson 3, and look at the worst cases, not the average.
  • Treat restored images as a way to see, and segmentation masks as a hypothesis to check, not as measurements.
  • When the method goes in a paper, say which network, which version, and what it was trained on. A reviewer cannot evaluate “AI denoising”.

If you want to try this without a Nikon licence

Everything in the table has a free equivalent that runs on a laptop, or in a browser, and that you can read the source of.

  • ZeroCostDL4Mic (von Chamier and colleagues, 2021): Google Colab notebooks that train and run CARE, Noise2Void, StarDist, Cellpose, U-Net and more on your own images, with no installation and a free GPU. The best first step.
  • Cellpose: pip install cellpose, open the GUI, drop in an image. Fine-tune on a few of your own corrected masks when the default model is not good enough.
  • napari: the Python image viewer that most of these tools plug into, and the place to look at your results slice by slice.
  • BioImage Model Zoo: pre-trained models for microscopy, in a format that runs in Fiji, ilastik, napari and DeepImageJ.
  • For calcium imaging specifically, Suite2p and CaImAn do registration, cell detection and spike inference end to end, and are what the labs producing the datasets on this site actually use.

The vendor tools earn their price by being there at the moment of acquisition, with support, inside the software the microscope already runs. The open tools earn their place by being inspectable, citable and free, which is what a result needs to be when it leaves the lab. Most imaging groups I know end up using both, and the ones who understand the lineage above use both well.

Base64 to image: what it is, how to do it, and when you should

In 2018 I spent some time working out how to move medical images, TIFF files among them, between services and into web pages without a separate request for every picture. Base64 turned up everywhere: in data URIs, in JSON payloads, in the FHIR healthcare standard. This is the article I wish I had read first: what Base64 actually is, a worked example small enough to check by hand, working code in Python, Java and JavaScript, and an honest account of when inlining an image as text is a good idea and when it is not.

What Base64 is

Computers store everything as bytes, but a surprising number of the channels we push data through were designed for text: email, HTML, JSON, URLs, XML, configuration files. Hand them raw image bytes and something along the way may treat a byte as a line break, a control character, or the end of the message. Base64 solves this by rewriting any sequence of bytes using only 64 safe characters: A to Z, a to z, 0 to 9, + and /, with = as padding at the end.

The mechanism is simple. Eight bits per byte, six bits per Base64 character, so every three bytes become four characters. Here is the classic example, the word Man:

Input bytesMan
As numbers7797110
As 8-bit binary010011010110000101101110
Regrouped into 6 bits010011   010110   000101   101110
As numbers 0 to 6319   22   5   46
Looked up in the alphabetT   W   F   u

Man becomes TWFu. When the input is not a multiple of three bytes, the last group is padded with zero bits and one or two = signs mark how much padding there was: Ma becomes TWE= and M becomes TQ==. The cost is that four characters carry three bytes of information, so Base64 output is always about a third larger than the input. A 1.9 KB image becomes 2.5 KB of text.

Two things Base64 is not. It is not compression: the output is bigger, never smaller. And it is not encryption, even though it looks like gibberish and often appears next to cryptographic material. Anyone can turn a Base64 string back into the original bytes with one function call, so it protects nothing. It is a transport format, and that is all.

Image to text and back, in Python

The Python standard library has done this since the beginning. Open the file in binary mode, encode, and turn the resulting bytes into a string.

import base64

with open("brain.png", "rb") as f:          # "rb": read the raw bytes, not text
    raw = f.read()

text = base64.b64encode(raw).decode("ascii")   # bytes in, bytes out; .decode turns them into a str
print(len(raw), "bytes of PNG ->", len(text), "characters of Base64")
print(text[:60] + "...")
# 1904 bytes of PNG -> 2540 characters of Base64
# iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAADAFBMVEU7PT4g...

Every PNG file starts with the same eight bytes, so every Base64-encoded PNG starts with iVBORw0KGgo. Once you have seen that prefix a few times you will recognise an inlined PNG anywhere. JPEGs start with /9j/.

To put the image in a web page, prefix the text with a small header that says what it is. This is a data URI, and browsers accept it anywhere they accept a normal image address.

data_uri = "data:image/png;base64," + text
html = f'<img src="{data_uri}" alt="a brain">'
print(html[:80] + "...")
# <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAD...
The site icon, delivered as Base64 text inside this article
This image is not a file on the server. It is 2,540 characters of Base64 text sitting in the HTML of this article, and your browser decoded it.

Going the other way is one call to b64decode. The decoded bytes are a PNG file in memory; BytesIO lets Pillow open them as if they were on disk, and from there they are a NumPy array, which is where every image analysis on this site begins.

import io
import numpy as np
from PIL import Image

raw_again = base64.b64decode(text)
print("round trip identical:", raw_again == raw)

img = Image.open(io.BytesIO(raw_again))        # BytesIO makes bytes in memory look like a file
pixels = np.array(img.convert("RGB"))          # a NumPy array: rows x columns x (red, green, blue)
print(img.format, img.size, pixels.shape, pixels.dtype)
# round trip identical: True
# PNG (32, 32) (32, 32, 3) uint8

The same thing in Java and JavaScript

The original version of this article used the Apache Commons codec and got the last line wrong: Base64.encodeBase64(bytes).toString() returns the memory address of a byte array, not the encoded text, which is an easy mistake to make and a hard one to notice until something downstream fails to decode. Since Java 8 the standard library has a correct and simpler answer.

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

// Java 8 or later: no third-party library needed
byte[] raw = Files.readAllBytes(Paths.get("brain.png"));
String text = Base64.getEncoder().encodeToString(raw);
byte[] rawAgain = Base64.getDecoder().decode(text);

In the browser the job is usually the reverse: a user picks a file and you want to show it, or send it to an API, without uploading it first. FileReader.readAsDataURL produces the complete data URI directly.

// In the browser: read a file the user picked, straight to a data URI
const file = document.querySelector("input[type=file]").files[0];
const reader = new FileReader();
reader.onload = () => { document.querySelector("img").src = reader.result; };
reader.readAsDataURL(file);   // result looks like "data:image/png;base64,iVBOR..."

When to inline an image, and when not to

The reason I went looking at this in the first place was web performance. Every image on a page used to cost a separate HTTP request, and in the HTTP/1.1 era, where a browser would open only a handful of connections to a server, a page with fifty small icons spent most of its load time waiting in line. Steve Souders built a career on documenting this, and the standard fixes were sprites (pack all the icons into one image and show a slice of it with CSS) and inlining (put the image in the page as a data URI, so it arrives with the HTML). Sprites work but are painful to maintain, and for large or unusual formats such as TIFF they do not work at all. Inlining looked like the way out.

It is, for some images. The honest trade-off in 2026:

  • Inline tiny images. Icons, logos, a one-pixel placeholder, anything under a couple of kilobytes. The 33 percent size penalty is a few hundred bytes, and saving a round trip is worth more than that.
  • Serve everything else as a file. A large inlined image bloats the HTML, cannot be cached separately from the page, cannot be lazy-loaded, and blocks the page from rendering until it has fully arrived. A file is fetched in parallel, cached once, and reused across pages.
  • HTTP/2 changed the maths. Requests are cheap now; many can share one connection. The case for sprites has mostly gone, and the case for inlining has shrunk to the very small.
  • APIs are the exception. When an image has to travel inside JSON or XML, Base64 is the only option, and the size penalty is the price of using a text format. This is why the FHIR standard for health data carries images and documents as Base64 in its Attachment and Binary resources, and why a great deal of medical imaging code is really Base64 plumbing.

Mistakes to watch for

  • Bytes versus strings. In Python 3, b64encode takes bytes and returns bytes. Forgetting the .decode() puts b'...' into your JSON. Forgetting to open the file with "rb" gives you a decoding error before you even start.
  • There is more than one alphabet. The URL-safe variant swaps + and / for - and _, because the standard characters have meanings inside a URL. Python has urlsafe_b64encode for it. Decoding one variant with the other fails.
  • Line breaks. Email-style encoders wrap the output every 76 characters. Most decoders tolerate the newlines; some do not. Strip them if in doubt.
  • Missing padding. Some systems drop the trailing = signs to save space. If a decoder complains about “incorrect padding”, append = until the length is a multiple of four.
  • Treating it as security. See above. If a system hides a password behind Base64, it is not hidden.

A note on image search

The title of the original article promised image search, and it is worth being clear about what Base64 contributes to that: nothing. The encoded text is a faithful copy of the bytes and carries no more meaning than they do. Searching for similar images means computing something from the pixels, a hash for exact duplicates or an embedding from a neural network for visual similarity, and comparing those. Base64 is just how the picture gets to the service that does the computing. That work, turning pixels into vectors you can compare, is a topic for the machine learning track of this site.