Back in the winter of 2017/2018, I backed a Kickstarter project called qoo cam. The promise was simple but ambitious: a compact digital camera capable of capturing both spherical 360° panoramas and 180° stereoscopic images. The campaign was successfully funded, the camera went into production, and in August 2018 I finally held the Qoo Cam in my hands.

My original plan was to use this camera for a very specific workflow: mounting the qoo cam on a tripod, triggering it remotely via an app, and shooting exposure brackets that could be merged into 360° HDRI panoramas. Those panoramas would then serve as the lighting environment for photorealistic 3D renders with natural light and shadow.
Straight out of the camera, each capture generates a single JPG (or DNG) at 4320 × 2160 pixels, with two fisheye exposures side by side. To use these as spherical panoramas, you have to convert them into equirectangular format—that is, remap the paired-fisheye image into a rectangular projection.

The manufacturer provides Qoo Cam Studio for exactly this conversion,1 and in theory it automates the process. Unfortunately, in practice the remapping is ‘erratic’. The best way to illustrate this is with a simple GIF: I imported two Qoo Cam Studio–exported frames into Photoshop on separate layers, then toggled each layer on and off in turn.
Ignore the shifting foliage and clouds—instead, focus on the stone floor in the foreground and, especially, the concrete wall on the right. You can see that the “straightening” algorithm varies from frame to frame. If you’re working with video or single stills, that inconsistency is merely annoying; but my workflow depends on blending 10 to 20 differently exposed stills. If key elements like the horizon or static foreground features jitter around each time you switch exposures, Qoo Cam Studio becomes unusable for creating static HDR images by exposure stacking.
Building my own unwrapper
To solve this, I built a small Python tool that renders Qoo Cam Studio obsolete (at least for my personal workflow/use case). It applies a single, constant remapping function to every capture. With this brute force approach I’m willing to accept minor stitching artifacts along the seam, because it guarantees that all static objects stay perfectly aligned in the spherical panorama, precisely what I need for consistent HDRI bracketing.
The tool lets you specfiy a path to directory and circles through all JPG files, converts them to an equirectangular panorama and exports each image in a subfolder.
You find the complete code on GitHub, but here are some details that may be relevant to you.

If we look again at the unedited raw image out of the camera, three aspects become obvious:
- the images need to be rotated so that the floor is facing down and the ceiling/sky is facing up (duh!)
- the right image needs to be mirrored horizontally
- when converting, you can’t assume that the image circle of each fisheye image is
[height of image]/2
because there is quite a bit of blur and vignetting happening at the edges
I did quite a bit of trial and error, but I achieved the best results by assuming the following values:
radius1 = 912
radius2 = 912
angle1 = 270 # degrees
angle2 = 90 # degrees
mirror1 = False
mirror2 = True
Hint: the values for radius1
and radius 2
may vary from 910–915.
Since we lose some vertical resolution the Python script provides a naive upsampling so that the exported images feature a resolution of 4320 x 2160px.
Metadata
Since a lot of HDR merge tools rely on correct exposure values (such as f-stop, exposure time, etc.) embedded in the images’ EXIF metadata, the Python script tries to copy the relevant values from the source image to the exported equirectangular panorama.
orig = Image.open(input_path)
if "exif" in orig.info:
ex = piexif.load(orig.info["exif"])
new_exif = {"0th":{}, "Exif":{}, "GPS":{}, "Interop":{}, "1st":{}, "thumbnail":None}
# copy ExposureTime, FNumber, ExposureBiasValue if present
for tag in (piexif.ExifIFD.ExposureTime,
piexif.ExifIFD.FNumber,
piexif.ExifIFD.ExposureBiasValue):
val = ex["Exif"].get(tag)
if val is not None:
new_exif["Exif"][tag] = val
exif_bytes = piexif.dump(new_exif)
Results
Running the script takes approx. 60 seconds for 15 source images. This is not particularly performant but it gets the job done. When loading these files into Photoshop via the Merge To HDR Pro command, Photoshop correctly reads the exposure values from the EXIF metadata and is able to merge the images without any hassle.

The results are okay-ish. I am aware that there are some visual gaps/unalignments where the two images are stiched but overall the images are quite usable.
Since the pixel resolution of the qoo cam provides only 4K at maximum, the HDRI won’t be used as high fidelity backdrops in the rendered scenes anyway, but they may provide enough data to light a 3D scene properly.

Footnotes
- As of today (June 2025) it seems that the current version of Qoo Cam Studio does not support the first (original) version of the Qoo Cam hardware any more. (At least I was unable to export any meaningful footage from the tool that was created by the original Qoo Cam.) ↩︎