Skip to content

Instantly share code, notes, and snippets.

@typebrook
Last active June 1, 2023 04:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save typebrook/87babb88170f85607a29364712c108e0 to your computer and use it in GitHub Desktop.
Save typebrook/87babb88170f85607a29364712c108e0 to your computer and use it in GitHub Desktop.
Insta 360 Panorama solution with https://github.com/trek-view/fusion2sphere
input/
output/
upload/
**/.git
*/
input/
*.gpx
track
fusion2sphere
tmp
photos.geojson
metadata
install

Quick started

# Prepare raw data (dula-fish eyes images from insta360)
# and put them into "input/"
mkdir input/
cp /path/to/your/insta360-folders/*.insp input/

# Generate panorama images to "output/"
make panorama

# You may filter out some unecessary images from "output/" by some reasons
# Put images you want to upload to Kartaview and Mapillary into "upload/"
cp -r output/ upload/

# This generate GeoJSON file for each image location
# You can read it on other softwares (like geojson.io) to make sure locations in EXIF are correct
make photos.geojson

# Check time period of images and GPX file
make stat_exif
GPX=your-gpx-file make stat_gpx

# IF you want to correct locations by time with GPX file, try command 'fix-gps':
#   1. Specify GPX file by variable 'GPX'
#   2. If GPX file contains track point which time is same to image, 
#      then location in image EXIF would be modified.
#
#      Image would be rename with '_modified', for example:
#        2022_0807_999.jpg -> 2022_0807_999_modified.jpg
#   3. If you want to add track points by interpolation, 
#      set variable INTERPOLATION=true 
GPX=your-gpx-file INTERPOLATION=true make fix-gps

# Now generate a new GeoJSON file and check again
make photos.geojson

# Upload images in "upload/" to Kartaview
make upload
#! /bin/bash
# Usage:
# ./add-gps-into-exif test.jpg TRACKFILE
#
# TRACKFILE should contains fields "epoch", "longitude" "latitude" "elevation"(optional) in each line
# You can specify REMOVE_UNMATCHED=true to remove unmatched images
image="$1"
track="$2"
# Get Time and epoch from image
time=`exiftool "$image" -s -s -s -CreateDate`
epoch=`date +%s -d ${time:0:4}-${time:5:2}-${time:8:2}T${time:11:8}Z$TZ`
record="`grep ^$epoch $track | head -1`"
if [ -n "$record" ]; then
read _ lon lat ele <<<"$record"
echo $epoch $lon $lat
exiftool $image \
-overwrite_original \
-gpslongitude=$lon \
-gpslatitude=$lat \
-gpslongituderef=E \
-gpslatituderef=N \
&>/dev/null
mv $image ${image%.*}_modified.${image#*.}
elif [[ $REMOVE_UNMATCHED == true ]]; then
echo rm $image
rm $image
fi
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install -y \
git \
make \
gcc \
libjpeg-dev \
imagemagick \
exiftool \
jq \
proj-bin \
curl \
python \
python3-pip \
bc
RUN pip3 install yq
COPY ./ /app
WORKDIR /app
RUN make fusion2sphere install
#! /bin/bash
while getopts 'i:o:' arg; do
case $arg in
i) INSP=$OPTARG ;;
o) OUTPUT_DIR=$OPTARG ;;
esac
done
if [ -z $INSP ]; then
echo Please give Insta 360 file with '-i' option
exit 1
elif [ -z $OUTPUT_DIR ]; then
echo Please give output dir with '-o' option
exit 1
fi
OUTPUT_DIR=${OUTPUT_DIR%/}
mkdir -p $OUTPUT_DIR
img=`basename ${INSP%.insp}`
origin=/tmp/${img}_origin.jpg
left=/tmp/${img}_1.jpg
right=/tmp/${img}_2.jpg
output=${OUTPUT_DIR}/${img}.jpg
trap "rm $origin $left $right &>/dev/null" EXIT
convert $INSP $origin
convert $origin -crop 3040x3040+0+0 -rotate 90 $left
convert $origin -crop 3040x3040+3040+0 -rotate 270 $right
./fusion2sphere -b 10 -w 5800 -f $left $right -o $output photo-mode.txt
exiftool &>/dev/null \
-overwrite_original \
-UsePanoramaViewer=TRUE \
-ProjectionType="equirectangular" \
-tagsFromFile $origin $output \
#! /bin/bash
set -o pipefail
echo $1
[ -z "$1" ] && echo 'Please specify GPX file as first argument' && exit 1
_get_interpolation() {
read epoch1 lon1 lat1 ele1 epoch2 lon2 lat2 ele2
DIFF=$(( $epoch1 - $epoch2 ))
for (( i=1; i<=$(( $DIFF - 1 )); i++ )); do
echo $(( $epoch2 + $i ))
echo 'scale=7; ' $lon2 '+ (' $lon1 - $lon2 ') *' "$i/$DIFF" | bc -l
echo 'scale=7; ' $lat2 '+ (' $lat1 - $lat2 ') *' "$i/$DIFF" | bc -l
echo 'scale=2; ' $ele2 '+ (' $ele1 - $ele2 ') *' "$i/$DIFF" | bc -l
done | paste -d' ' - - - -
}
xq -r \
--xml-force-list trk \
--xml-force-list trkseg \
--xml-force-list trkpt \
'.gpx.trk[].trkseg[].trkpt[] | "\(.time) \(."@lon") \(."@lat") \(.ele)" ' \
"$1" >track_info.tmp && \
paste -d' ' <(cut -d' ' -f1 track_info.tmp | date +%s -f -) <(cut -d' ' -f2-4 track_info.tmp) | \
while read epoch record; do
if [ "$INTERPOLATION" = true ] && (( $epoch - ${LAST_EPOCH:-$epoch} > 1 )); then
echo $epoch $record $LAST_EPOCH $LAST_RECORD | _get_interpolation
fi
echo $epoch $record
LAST_EPOCH=$epoch
LAST_RECORD=$record
done >track
rm track_info.tmp
.ONESHELL:
.PHONY: panorama photos.geojson metadata upload
SHELL := bash
IMAGE := street-view:1.1
HUGO := klakegg/hugo:0.101.0
export INPUT_DIR?=input
export OUTPUT_DIR?=output
export UPLOAD_DIR?=upload
stat_exif:
@exiftool $(INPUT_DIR)/* -s -s -s -CreateDate | \
sed '/^==/d;$$d' | \
sort | \
sed -n -E '1s/^/Start: \t/; 1p; $$s/^/End: \t/; $$p'
stat_gpx:
@xq -r \
--xml-force-list trk \
--xml-force-list trkseg \
--xml-force-list trkpt \
'.gpx.trk[].trkseg[].trkpt[].time' $(GPX) | \
date --iso-8601=seconds -f - | \
sort | \
sed -n -E '1s/^/Start: \t/; 1p; $$s/^/End: \t/; $$p'
panorama: fusion2sphere
count=$$(ls $(INPUT_DIR)/*insp | wc -l)
find -L $(INPUT_DIR) -name '*insp' | nl | \
while read index insp; do
echo $$index/$$count
./insp2panorama -i $$insp -o $(OUTPUT_DIR) &
while [ $$(jobs -r | wc -l) -ge $$(nproc) ]; do
sleep 0.3
done
done
repo-fusion2sphere:
git clone --depth=1 git@github.com:trek-view/fusion2sphere $@
fusion2sphere: repo-fusion2sphere
make -C $^
ln -sf $^/$@ ./$@
photos.geojson:
find -L $(UPLOAD_DIR) -name '*.jpg' | \
xargs exiftool -s -s -s -gpslongitude -gpslatitude -gpsaltitude -CreateDate | \
sed '/^=/d; $$d; s/ m Above Sea Level//; s/deg/d/; /d/ s/ //g; s/ /_/' | \
paste -d' ' - - - - | \
sort -k4 | \
cs2cs -f %.7f +init=epsg:4326 +to +init=epsg:4326 | \
awk '{ print "[" $$1 "," $$2 "," $$3 "]" }' | \
jq -s '{type: "Feature", properties: {}, geometry: {type: "LineString", coordinates: .}}' >$@
export INTERPOLATION?=false
track:
./make-track-from-GPX $(GPX)
export TZ=+0800
export REMOVE_UNMATCHED?=false
fix-gps: track
find -L $(UPLOAD_DIR) -name '*jpg' | \
while read file; do
./add-gps-into-exif $$file $< &
while [ $$(jobs -r | wc -l) -ge $$(nproc) ]; do
sleep 0.3
done
done
upload-scripts/osc_tools.py:
git clone --depth=1 git@github.com:typebrook/upload-scripts
install: upload-scripts/osc_tools.py
pip3 install -r upload-scripts/requirements.txt && touch $@
upload: upload-scripts/osc_tools.py
$< upload --path $(UPLOAD_DIR)
metadata:
sequence_id=`jq -r .id $(UPLOAD_DIR)/osc_sequence_id.txt`
curl https://api.openstreetcam.org/2.0/sequence/$${sequence_id}/photos | \
jq . >$@
clean:
rm -rf upload-scripts fusion2sphere install track
docker: repo-fusion2sphere upload-scripts/osc_tools.py
[ -z "`docker images -q $(IMAGE)`" ] && docker build . -t $(IMAGE)
docker run -it --rm \
--user `id -u`:`id -g` \
--volume /etc/passwd:/etc/passwd:ro \
--volume /etc/group:/etc/group:ro \
--volume `pwd`:/app \
$(IMAGE) $(COMMAND)
# left/front - 3104 x 3000
IMAGE:
RADIUS: 1450
CENTER: 1520 1520
FOV: 190
# right/back - 3104 x 3000
IMAGE:
RADIUS: 1450
CENTER: 1520 1520
FOV: 190
# recommended output width = 5760 (gives final dimensions of 5760 × 2880)
# 0.25 pt = 1440, 0.75 pt = 4320
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment