naipng
is a Python library and command-line tool to read NovelAI
data encoded in PNG files (like in Lorebook cards and generated images), with no dependencies.
Also check out my website pngmeta for a browser-based tool for
viewing and copying PNG tEXt
metadata.
User-made content for the web service NovelAI is often shared off-platform in the form of files. These files take on multiple formats. Though most are simple JSON, some content can be shared embedded within PNGs, which is more complicated to extract.
Text Generation: NovelAI allows exporting certain settings and objects related to text generation AI as PNG images in place of regular JSON files, as a way of associating art with the descriptions of characters and places being shared. These are commonly known as Lorebook cards.
Image Generation: NovelAI encodes image generation settings within generated PNGs, including parameters
such as prompt
, steps
, and so on, to make it easier to replicate and modify generated images.
Both domains use PNG metadata to encode this information, thus this tool allows the extraction of that metadata.
See more technical details in the Technical Background section.
naipng
is available on PyPI, so it can be installed through pip
:
python -m pip install naipng
Since naipng
has no dependencies, you can also import or run it by simply adding the naipng
directory
to your source tree.
naipng
may be used either as a library or a command-line tool.
The primary function entrypoint provided by naipng
is naipng.read(file: bytes | BinaryIO)
.
This can be used to parse a PNG image for NovelAI metadata from either an open file or a bytes
object in memory.
Two more specific variations of naipng.read()
also exist:
naipng.read_text_gen(file: bytes | BinaryIO)
only returns text generation data- E.g. Lorebooks
naipng.read_image_gen(file: bytes | BinaryIO)
only returns image generation data- E.g. image prompts
import naipng
# Using a file-like object
with open("image.png", "rb") as file:
decoded = naipng.read(file)
if decoded is None:
# No NovelAI data found encoded in the image
print("N/A")
else:
# naipng.read() returns a dict object representing the first data found
for k, v in decoded.items():
print(k, "->", v)
Another example, using bytes
as input to naipng.read
:
import naipng
import pathlib
data: bytes = pathlib.Path("image.png").read_bytes()
decoded = naipng.read(data)
# ...
naipng
defines two error types for naipng.read()
and its variants:
naipng.InvalidPNGError
is raised when a PNG is invalid and cannot be parsed, such as when:- The PNG datastream ends prematurely (before
IEND
) - A
tEXt
chunk is corrupted (i.e. has an invalid CRC) - The PNG signature is missing
- The PNG
IHDR
chunk is missing or misplaced
- The PNG datastream ends prematurely (before
naipng.NAIDataError
is raised when a PNG has atEXt
chunk designated asnaidata
, but it was unable to be decoded properly- This is never raised for
naipng.read_image_gen()
- This is never raised for
Both error classes derive from ValueError
.
This example shows the behaviour of naipng.read
with an invalid PNG datastream
(correct signature, but ends early).
import naipng
# Using a bytes object as input (a file-like object could be used too)
with open("image.png", "rb") as file:
# The datastream is truncated, rendering it invalid
data = file.read(10)
try:
naipng.read(data)
except naipng.InvalidPNGError as e:
raise SystemExit(f"Error: {e}") from e
This outputs:
Error: not a valid PNG file: ends prematurely
naipng
may be invoked on the command line as either python -m naipng <file>
or simply naipng <file>
.
usage: naipng [-h] [-q] [-c] [-t] [-i] file [outfile]
read NovelAI data encoded in a PNG file
positional arguments:
file PNG file to read, or - for stdin
outfile output file path, or - for stdout (default: -)
options:
-h, --help show this help message and exit
-q, --quiet don't print errors
-c, --compact don't pretty-print decoded JSON
-t, --text only check for text generation data
-i, --image only check for image generation data
- Printing to stdout:
naipng image.png
- Saving to a file:
naipng image.png naidata.json
- Saving to a file by redirecting
stdin
andstdout
:
naipng - < image.png > naidata.json
- Downloading via
curl
and piping PNG data throughstdin
:
curl -fs https://files.catbox.moe/3b6dux.png | naipng -
naipng
may be used in shell pipelines alongside JSON-parsing tools like jq
in order to construct more complex scripts.
$ curl -fs https://files.catbox.moe/3b6dux.png | naipng -c - | jq -r ".[\"entries\"][][\"text\"]"
Everyone said that no man now living or ever after would be born who would be equal to him in strength, courage, and in all sorts of courtesy, as well as in boldness and generosity that he had above all men, and that his name would never perish in the German tongue, and the same was true with the Norsemen
The linked file is the first Lorebook card shared as an example when PNG embedding was announced. Its art is by Tarmillustrates. The quote in it is from Þiðreks saga.
PNGs are made up of a sequence of data chunks. Beyond those used to store visual information (e.g. pixels, palettes), there are also several varieties of metadata chunks. See the official PNG specification for full details.
NovelAI uses tEXt
metadata chunks
to encode most metadata.
- For text generation settings, NovelAI uses a
tEXt
chunk with anaidata
keyword.- The contents are base64-encoded JSON.
naidata
is a nonstandardtEXt
keyword, so recognizing these chunks is unambiguous.
- For image generation settings, NovelAI uses multiple
tEXt
chunks, each with different keywords.- Some of these include
Title
,Description
,Software
,Source
, andComment
. naipng
only reads theComment
field among these, which is JSON-encoded and contains the most information.Comment
is a standardtEXt
keyword, so recognizing these chunks is slightly ambiguous.- Other software may use the
Comment
tEXt
chunk type for other purposes, and may or may not store JSON in it. naipng
assumes that the first JSON-encodedComment
tEXt
chunk found is valid image generation metadata.
- Other software may use the
- Some of these include
naipng
is free and open-source software provided under the zlib license.