- Latest app-release.apk (2016-04-08)
Note: if you've previously installed a debug.apk,
you'll need to uninstall it first,
as this release.apk is signed with a different key.
I intend to use this same release key from this point forward.
- Older source tarball (2015-10-08)
Includes diff with Therm App SDK 2.0.1 imported into Android Studio.
Includes copy of pngj.
Sorry, no support.
What is ThermApp?
It is a thermal imaging camera for Android devices,
available from therm-app.com.
What is ThermApp Wild?
In order to use the ThermApp camera, you need an Android App that can
talk to the camera, like the default
Opgal have made the SDK available to developers, so I decided to write my
own app, which I'm informally calling "ThermApp Wild" (for "wildlife").
Why write your own App?
I found the basic app lacking in some ways that seemed simple to address.
Also, I wanted features customized to my own use for wildlife detection, often
in the cold (thus "glove mode").
For more information, see my blog:
Why isn't it polished or on Google Play?
Since this is just a side activity for personal use, I didn't want to deal
with unnecessary distractions, like appearance (getting an app to "look good"
is an annoyingly time-consuming aspect of app development), tech support, etc.
But I don't mind sharing the app, or even the source code, as long as you
accept it "as is".
Are there other apps available??
If you don't already know about it, check out also Bill's
ThermApp Plus, currently available for Beta testing.
- PIC: saves the current image to a file named tw#.jpg
Additionally, a lossless grayscale PNG (more below)
is saved as tw#.t.png if enabled in the preferences.
- Hot: enables hotspot detection.
When detected, (i) a sound will be played, (ii) the frame will be
displayed in an inset window, and (iii) hotspot detection will be
disabled. Tap the inset window to dismiss it; tapping the "Hot"
button again will also dismiss the inset (and re-enable hotspot
The hotspot detection algorithm and parameters are described
- BG: Background noise filter.
- Press "BG" to enable/disable background filtering, if a background
image has been taken.
- Long-press "BG" to capture the next seven frames as the background
noise filter. This filter will also be saved (as
bg.t.png) for future use, though I have found this never
as good as taking a new image.
- When saving a picture while background filtering is enabled,
the filename will be tw#b.jpg with an extra 'b' for
- Best practice (best as I can tell) for taking a background image
is to hold the camera close to a surface of uniform temperature
(like an area of snow) -- such that moving the camera around
slightly yields no perceived changes in readings (other than the
sensor's apparent internal cycling effect of some sort).
- I've found that the background changes with sensor temperature,
getting noticeably worse in extreme cold (errors up to 5°C!).
To compensate, retake the background image when new noise lines
- I've also found that the background delta is non-linear w.r.t.
the target temperature; i.e., taking the background image against
a warm target will reveal noise when viewing a cold target, and
vice versa. For best results, take the background image on a
surface close to the target temperature.
- Volume button ("glove mode"): To support control when wearing
a glove in the cold, I've remapped the following actions on the volume
* To distinguish single from double click, it waits at least 0.6 seconds
after the initial click to see if a second one happens.
This means that after a single click, there will be a 0.6 second delay
before the action happens.
- Long Press on either volume button: equivalent to pressing
"PIC" when released.
I found that engaging the "take photo" action when first
pressing the button results in too much shake.
- Volume Down Single Click*: equivalent to pressing "HS".
- Volume Up Single Click*: equivalent to pressing "BG".
- Double Click on either button: equivalent to long-pressing
"BG" (to enable taking background image).
- Min/max lock: when background or hotspot is enabled, the min/max
temperatures will be shown (it is not shown by default because we don't
bother traversing the frame to compute them). Long-pressing either of
those temperatures will lock it, with the locked value shown in
[brackets] and actual min/max in (parentheses). Long-press again to
- Not currently supported: taking video is the biggest missing
feature compared to the basic ThermApp app.
- A lossless PNG is a grayscale PNG image whose color values and meta-data
can exactly reconstruct the original temperature readings.
- Currently, its data is landscape oriented, with the short dimension
in reverse (i.e., reflected). This reflects the scan order of
the data from the device, and I'd rather not tweak this (even though
it would be easy). The jpg image will not be reflected this way.
- Its range is stretched to fit in the grayscale bitwidth (8 or 16 bits)
via an integer multiple; as such, it is likely not the best rendering for
visual consumption. Stretching its histogram is a simple first step that
gets you closer to a good rendering.
- Its metadata "Comment" field includes the min/max temperature
(in units of .01 C), etc.
- FWIW, I'm suffixing all "lossless ThermApp PNG" files with .t.png.
- I have a small java utility that can convert this file to/from
ThermApp's raw array of temperatures (which I may or may not
get around to packaging up in a shareable way).
- Uses pngj library.
- Some examples here
The current hotspot-detection algorithm divides the frame into tiles of
The max temperature of each tile is recorded, unless it is at the edge
of the frame.
A tile is hot if it is in the top 10% of the temperature range.
A hot frame is reported when all of the following conditions are satisfied:
All of the numbers in bold are default parameters that can be configured in
the preferences menu.
- The hot tiles all occur within a 2x2 block (i.e., there are at most
4 hot tiles, and they're all confined to one area);
- The lowest hot tile temperature is 10% warmer than the highest
non-hot tile -- excluding a 1-tile buffer around the hot block.
- The reported temperature spread is at least 2.5C.
- 2 successive frames satisfy the above conditions.
The idea behind this algorithm is to detect a relatively small (localized)
object that is significantly warmer than the surroundings. Excluding edge
temperatures (condition  above) handles the case when, e.g., moving
from a large region of cool background to a large region of warm background,
there could be a frame where only a small piece of the large warm region is
present at a corner or edge of the frame. Waiting two successive frames is
also meant to filter out similar false positives.
The tile-based approach is also chosen for performance reasons (it is more
probabilitic in nature than, say, a histogram-based approach). Because I'm
coding entirely in Java, it turns out that for the 25Hz version I can only
afford to iterate the full temperature frame once.
Roughly in priority order:
- min/max manual temperature adjustment.
- hotspot-based max temperature setting.
- new color palettes.
- interpolate background delta from two background images.
- non-linear color mapping (probably what the current "night vision"
mode does, except in color).
- take video.
At this point I'm not likely to implemented these because a Java-based
implementation will likely be too slow for the 25Hz version (though still
acceptable with the 9Hz version), and I'm too lazy to figure out how to work
with the NDK.
Also, Bill has already done it in his
- min/max locking (but no adjustment afterwards).
- "tile"-mode hotspot detection replaces histogram-based ones.
- when a hotspot is detected, the image is rendered in an inset window.
- switch to using a signed release.apk instead of a debug.apk.
- saved images prefixed with "tw" instead of "ta".
- ported to ThermApp SDK 2.0.1 with Lollipop support.
- hotspot detection.
- option to rotate display (0,90,180,270).
- saved jpg reflects device orientation.
- option to disable saving lossless PNG.
- Note: button behaviors have changed.
- apparently fixed bad pixels in background that sometimes happens when
it is too cold.
- save both png and jpg.
- initial version: saves lossless PNG in portrait orientation.