Category Archives: Photography

Kodi push pictures/directory from website

For a new project i’m using parts of my Photo Manager.

UPDATE: Random photo push from directory in one script.

I will post the Minimal code for the following:

  • Caching thumbnail generation
  • Drag and drop image for displaying on a kodi instance
  • Button to start slideshow of a directory
  • Open image in browser with obfuscated image url (not shown in movie)
  • Stop image playing

Json RPC used:

# Playing a single file
curl -H "content-type:application/json" -i -X POST -d '{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"file":"'.$dir2.'"}}}' http://KODI-IP:8080/jsonrpc &';

# Playing a directory
curl -H "content-type:application/json" -i -X POST -d '{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"directory":"/path/'.$dir.'"}}}' http://KODI-IP:8080/jsonrpc &';

# Stop playing (i'm stopping player 1 and 2) You can query which player is active, this works also 
curl -H "content-type:application/json" -i -X POST -d '{"jsonrpc": "2.0", "method": "Player.Stop", "params": { "playerid": 1 }, "id": 1}' http://10.1.0.73:8080/jsonrpc';
Kodi enable http control and disable authentication (if you want to use authentication, change the curl commands accordingly)

Below the multiple PHP files, i’ve removed a lot of code specific for my manager.

By the way, I love this trick:

header("HTTP/1.1 204 NO CONTENT");

I’ve you put this in the top of your php script which is linked from the first page, you won’t open this link in your browser, but it gets executed nevertheless!

::::::::::::::
push.php
::::::::::::::
<!DOCTYPE html>
<html>
<head>
</head>
<body>

<div id="dropbox">Drop Image</div><br>
<a href="playpush.php?dir=TEMP/Sake">playdir</a> 
<a href="stoppush.php">stop</a> 

# Here i have a generated part to list my photoalbum photos in this format

<a href='getimg.php?imagepath=/mnt/fileserver/TEMP/1.jpg'><img src='getimg2.php?imagepath=/mnt/fileserver/TEMP/1.jpg' width=300 title='1.jpg'></a>
<a href='getimg.php?imagepath=/mnt/fileserver/TEMP/2.jpg'><img src='getimg2.php?imagepath=/mnt/fileserver/TEMP/2.jpg' width=300 title='2.jpg'></a>

<script
    type="text/javascript"
    src="javascript2.js"
    
  ></script>
</body></html>
::::::::::::::
getimg.php - Displays photo in browser (forgotten in movie)
::::::::::::::
<?php
Header("Content-Type: image/jpeg"); 
$file = $_GET['imagepath'];
$file = str_replace("%20", "\ ", $file);
$file = str_replace("(", "\(", $file);
$file = str_replace(")", "\)", $file);
$log =  'imggetlog';
file_put_contents($log, $file, FILE_APPEND);
file_put_contents($log, "\n\r", FILE_APPEND);
header('Content-Length: ' . filesize($file));
readfile($file);
?>

::::::::::::::
getimg2.php - makes a caching thumbnail 
/long/image/path/to/photo.jpg -> cachedir/longimagepathtophoto.jpg
::::::::::::::
<?php
Header("Content-Type: image/jpeg"); 
$file = $_GET['imagepath'];
$file = str_replace("%28", "\(", $file);
$file = str_replace("%29", "\)", $file);
$file = str_replace("%20", "\ ", $file);

$cachename = str_replace("/", "", $file);
$cachename = str_replace(" ", "", $cachename);
$cachename = "cachedir/$cachename";
$log =  'imggetlog';
file_put_contents($log, $file, FILE_APPEND);
file_put_contents($log, "\n\r", FILE_APPEND);
if (!file_exists("$cachename")) {

exec("convert -resize 300x300 \"$file\" \"$cachename\"");
}
header('Content-Length: ' . filesize("$cachename"));
readfile("$cachename");
?>

::::::::::::::
playpush.php - Pushes DIRECTORY play to Kodi
::::::::::::::
<?PHP
header("HTTP/1.1 204 NO CONTENT");

header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.

$dir=$_GET['dir'];

$command='nohup curl -H "content-type:application/json" -i -X POST -d \'{"jsonrpc":"2.0","id":1,"method":"Player.Open","params":{"item":{"directory":"/mnt/fileserver/'.$dir.'"}}}\' http://IPKODI:8080/jso
nrpc &';
exec($command, $output, $retval);
?>

::::::::::::::
stoppush.php - stops displaying
::::::::::::::
<?PHP
header("HTTP/1.1 204 NO CONTENT");

header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.

$command='curl -H "content-type:application/json" -i -X POST -d \'{"jsonrpc": "2.0", "method": "Player.Stop", "params": { "playerid": 1 }, "id": 1}\' http://IPKODI:8080/jsonrpc';
exec($command, $output, $retval);
$command='curl -H "content-type:application/json" -i -X POST -d \'{"jsonrpc": "2.0", "method": "Player.Stop", "params": { "playerid": 2 }, "id": 1}\' http://IPKODI:8080/jsonrpc';
exec($command, $output, $retval);
?>
::::::::::::::
javascript2.js
::::::::::::::
function getAllElementsWith(tag, attribute, value)
{
  var matchingElements = [];
  var allElements = document.getElementsByTagName(tag);
  for (var i = 0; i < allElements.length; i++)
  {
    if (value.indexOf(allElements[i].getAttribute(attribute)) != -1)
    {
      // Element exists with attribute. Add to array.
      matchingElements.push(allElements[i]);
    }
  }
  return matchingElements;
}

// onDrop
function onDrop(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    var imageUrl = evt.dataTransfer.getData("URL");
    var links = getAllElementsWith("a", "href", imageUrl);
    var image;
     console.log(links, evt);
    
    if(links.length){
        image = links[0].getElementsByTagName("img");
        if(image.length)
            imageUrl = image[0].getAttribute("src");
        else
            imageUrl = "#no-image";
    }
    
///    alert(imageUrl);
var res = imageUrl.replace(/getimg/, "pushplay2");

location.href = (res);

};

// onDragOver
function onDragOver(evt){
    evt.preventDefault();
}

var dropbox = document.getElementById('dropbox');
dropbox.addEventListener('drop', onDrop);
dropbox.addEventListener("dragover", onDragOver, false);

Random picture push

file=$(find /mnt/fileserver/examples -type f  | shuf | head -1)
post_data="{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"Player.Open\",\"params\":{\"item\":{\"file\":\"$file\"}}}"
curl --user user:pass  -H "content-type:application/json" -i -X POST --data "${post_data}" http://KODI-IP:8080/jsonrpc

Below a example of what your can do with the code above

Quick previewing negatives using OBS

I’m using a camlink to connect a Nikon camera to my PC, but you could use your webcam also for this trick.

Now i can activate the full screen projector in OBS to preview and sort the negatives on a big screen. (right click on you preview screen)

When connecting a camera to OBS you initially have a “negative” image.

Below trick inverts your image, after that you can use your camera controls or OBS colour correction filter to adjust.

I’m using gimp to invert the LUT png image, but most image editors can invert an image.

A LUTs is a LookUp Table. Color presets/filters for adjusting the image.

Hardcore method .. command line

convert input.png -channel RGB -negate invert.png

DLSR Camera versus Mobile Phone

Not talking about the quality but how much we are using the camera’s.

Reason for taking pictures:

  • Capture memorable events – DLSR
  • Simple snapshots to remember stuff – Mobile
  • HQ photos – shoots etcetera – you want to use DOF, a mobile can´t do this
  • Images for web/mattermost/whatsapp – mobile phone is sufficient

But we always take our phone with us … sometimes we forget to bring the Nikon to memorable events.

A simple count of pictures on my fileserver.

# Below creates a CSV file
for f in $(seq 2000 2022) ; do echo -n $f, ; find  */$f -type f 2>/dev/null | wc -l  ;done 

Notes:

2010 – We bought a new nikon
2019 – Busy planning buying a new house
2020,2021 – Covid
< 2010 – Mobile pictures are potato quality

I had my first android phone in 2009.
Phones before 2010 didn´t have good exif information, so the picture count is a little off in the graph above.

Two minute timelapse of oyster mushrooms growing.

#!/bin/bash
# capture images from Foscam ( 5 minute interval )
while true; do
wget -q "http://IP-WEBCAM:88/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=adminuser&pwd=adminpass" -O $(date +%Y%m%d%H%M).jpg
sleep 300
done

# from jpg to mp4
ffmpeg -f image2 -r 24 -pattern_type glob -i '*.jpg'   -vcodec libx264 -profile:v high444 -refs 16 -crf 0 -preset ultrafast -vf scale=1920:1080 paddo.mp4

If your ipcam can’t add timestamps, use below part to add text (replace in above script)

...
...
wget -q "http://IP-WEBCAM:88/cgi-bin/CGIProxy.fcgi?cmd=snapPicture2&usr=adminuser&pwd=adminpass" -O /tmp/1.jpg
convert -fill white -pointsize 30 -draw "text 10,30 '$(date)'" /tmp/1.jpg $(date +%Y%m%d%H%M).jpg
rm -f /tmp/1.jpg 
...
...

Automatic photo sorting with Gps location lookup

We take a lot of pictures, with our Nikon camera and our mobile phones.

(Apparently in 2019 5544 pictures)

Some stats

757 20190803 - on a single day (Holiday)

Average pictures per month
locate "/2019/" | egrep -i "photoalbum|gsm" | egrep -i "mp4$|jpg$" | grep -Eo '2[[:digit:]]{3}[[:digit:]]{2}[[:digit:]]{2}' | cut -c-6 |sort | uniq -c | sort -n | awk '{ sum += $1; n++ } END { if (n > 0) print sum / n; }'
461

Besides android pictures being automatically uploaded to our nextcloud, I’m using some apps and scripts to get pictures and movies stored on my fileserver. (bash scripts/andftp)

For sorting those media files, i made a sorting script.
(Today I added a location sorting addition using GPS information stored in the exif information.

  • jpg and jpeg (add your own extentions)
  • mp4 and mov (for mobile and nikon)
  • Sorts by camera model/year/date/location
  • tries to extract date from filename when not found in exifinfo
  • Sorts whatsapp media
  • Sorts Raw

INSTALLING

pip3 install reverse_geocoder
You need python3, exiftool, exiftime and mediainfo

copy below python script in ~/bin/reverse2.py

( need more info? change last print entry admin1/admin2)
[{‘lat’: ‘-39.45556’, ‘lon’: ‘173.85833’, ‘name’: ‘Opunake’, ‘admin1’: ‘Taranaki’, ‘admin2’: ‘South Taranaki District’, ‘cc’: ‘NZ’}]

import reverse_geocoder as rg
import sys

lat=sys.argv[1]
lon=sys.argv[2]

coordinates = (lat,lon)

results = rg.search(coordinates) # default mode = 2


#print (results)

for entry in results:
    print(entry['name'] + "(" +  entry['cc'] + ")")

And a bash script /usr/local/bin/exifsort.sh

#!/bin/bash
#set -x

reversepath=/home/henri/projects/reversegeo/reverse2.py

#RAW
rawcnt=`ls | grep -i  nef$ | wc -l`
if [ "$rawcnt" = "0" ] ; then
echo "no raw"
else
mkdir raw 2>/dev/null
ls | grep -i nef$ | while read ; do mv $REPLY raw ; done
fi


ls | egrep -i "jpg$|jpeg" | while read ; do 
 	location=""	
	getmodel=$(exiftool "$REPLY" |grep "Make " | awk '{ print $3 }')
	if [ "$getmodel" != "" ] ; then 
		getmodel=$getmodel/
	fi
	echo "$REPLY" | grep WA0 >/dev/null && getmodel=whatsapp/

	gpsinfo=$(exiftool -c "%+.6f" "$REPLY" |grep "GPS Position" | cut -d":" -f2 | tr -d ' ' | sed s/,/\ /g)
	if [ "$gpsinfo" != "" ] ; then 
		location=$(python3 $reversepath $gpsinfo | grep -vi load | sed s/\(NL\)//g)
	fi
	dater=$(exiftime "$REPLY" 2>/dev/null | egrep "Created|Digitized" | sed s/Digitized/Created/g | tail -1  | cut -c 16-19,21,22,24,25)
	if [ "$dater" = "" ] ; then 
#		echo "Trying from filename"
		dater=$(echo $REPLY | grep -Eo '2[[:digit:]]{3}-[[:digit:]]{2}-[[:digit:]]{2}')

		if [ "$dater" = "" ] ; then 
#			echo "Trying from filename - maybe without dashes"
			dater=$(echo $REPLY | grep -Eo '2[[:digit:]]{3}[[:digit:]]{2}[[:digit:]]{2}')

		fi
	fi
	if [ "$dater" != "" ] ; then 
		year=$(echo $dater | cut -c-4)
		mkdir -p "${getmodel}$year/${dater}/$location"
		mv "$REPLY" "${getmodel}${year}/${dater}/$location"
	else
		mkdir -p "${getmodel}unknowndate/$location"
		mv "$REPLY" "${getmodel}unknowndate/$location"
      	fi
done

ls | egrep -i "mov$|mp4$" | while read ; do 
	
 	location=""	
	getmodel=$(exiftool "$REPLY" |grep "Make " | awk '{ print $3 }')
	if [ "$getmodel" != "" ] ; then 
		getmodel=$getmodel/
	fi

	echo "$REPLY" | grep WA0 >/dev/null && getmodel=whatsapp/
	gpsinfo=$(exiftool -c "%+.6f" "$REPLY" |grep "GPS Position" | cut -d":" -f2 | tr -d ' ' | sed s/,/\ /g)
	if [ "$gpsinfo" != "" ] ; then 
		location=$(python3 $reversepath $gpsinfo | grep -vi load | sed s/\(NL\)//g)
	fi
	dater=$(mediainfo "$REPLY" | grep Encode | tail -1 | cut -f2- -d:  | cut -f3 -d" " | sed s/-//g)
	if [ "$dater" = "" ] ; then 
#		echo "Trying from filename"
		dater=$(echo $REPLY | grep -Eo '2[[:digit:]]{3}-[[:digit:]]{2}-[[:digit:]]{2}')

		if [ "$dater" = "" ] ; then 
#			echo "Trying from filename - maybe without dashes"
			dater=$(echo $REPLY | grep -Eo '2[[:digit:]]{3}[[:digit:]]{2}[[:digit:]]{2}')

		fi
	fi
	if [ "$dater" != "" ] ; then 
		year=$(echo $dater | cut -c-4)
		mkdir -p "${getmodel}$year/${dater}/$location"
		mv "$REPLY" "${getmodel}${year}/${dater}/$location"
	else
		mkdir -p "${getmodel}unknowndate/$location"
		mv "$REPLY" "${getmodel}unknowndate/$location"
      	fi
done

Example running in a directory with mixed media

# Raw images get moved into a RAW directory
no raw

# Samsung phone detected with date and GPS location
mkdir -p samsung/20220717/Hilversum
mv 20220717_133453.jpg samsung/20220717/Hilversum

# OnePlus phone
mkdir -p OnePlus/20021208/Voorburg
mv IMG_20190109_091825.jpg OnePlus/20021208/Voorburg

# Realme (Added country when not NL)
mkdir -p realme/20220607/Isle of Islay(GB)
mv IMG20220607213630.jpg realme/20220607/Isle of Islay(GB)

# Whatsapp has no date embedded so it get it from filename
Trying from filename
Trying from filename - maybe without dashes
mkdir -p whatsapp/20221021/
mv IMG-20221021-WA0000.jpg whatsapp/20221021/

# Nikon without GPS
mkdir -p NIKON/20220613/
mv DSC_1423.MOV NIKON/20220613/

# Whatsapp video without exif
mkdir -p whatsapp/20170528/
mv VID-20170528-WA0006.mp4 whatsapp/20170528/

# No camera name detected in exif from mobile movie
mkdir -p 20190114/Maarssen
mv VID_20190114_142455.mp4 20190114/Maarssen

# Location in mp4
mkdir -p 20220607/Lamlash(GB)
mv VID20220607155044.mp4 20220607/Lamlash(GB)

Result

./NIKON/2022/20220613/DSC_1423.MOV
./NIKON/2022/20220610/750_1101.JPG
./realme/2022/20220818/Hilversum/IMG20220818203825.jpg
./realme/2022/20220607/Isle of Islay(GB)/IMG20220607213630.jpg
./2019/20190114/Maarssen/VID_20190114_142455.mp4
./whatsapp/2017/20170528/VID-20170528-WA0006.mp4
./whatsapp/2022/20221021/IMG-20221021-WA0000.jpg
./2022/20220607/Lamlash(GB)/VID20220607155044.mp4
./2022/20220516/Hilversum/VID20220516125913.mp4
./OnePlus/2002/20021208/Voorburg/IMG_20190109_091825.jpg
./samsung/2022/20220717/Hilversum/20220717_133453.jpg

You could automate this using incrond
apt-get install incron
add your user to /etc/incron.allow
incrontab -e
add

/fileserver/mediain/ IN_CREATE /usr/local/bin/sortmymedia.sh
Coping a file in the directory, auto sort and move to correct location

sortmymedia.sh

#!/bin/bash
cd /home/user/media
/usr/local/bin/exifsort.sh

Workshop Cyanotype

The cyanotype (from Ancient Greek kuáneos, “dark blue” and túpos, “mark, impression, type”) is a slow-reacting, photographic printing technique. It produces a cyan-blue print used for art as monochrome imagery applicable on a range of supports, and for reprography in the form of blueprints. For any purpose, the process usually uses two chemicals: ferric ammonium citrate or ferric ammonium oxalate, and potassium ferricyanide, and only water to develop and fix. Announced in 1842, it is still in use.

This technique was also used as a method of copying drawings.
For example buildings and schematics. While making copies of drawings with the exact dimensions of the original, making the result untemperable was another big plus. ( You could not move/redraw walls for example on the copy)

I’ve printed a photo on transparant sheets to experiment with.
(Next time, i’ll take a larger size, and fix the contrast.

The most interesting ones i’ve made today:

Challenged myself into programming a photo quiz

Got bored, and challenged myself …

How about a quess the picture from our photo collection??
(123580 photos .. )
So i show a random picture, and when i press ESC it will show some information about the picture …
Quess the year and the event

Well i gave myself 15 minutes to program something ..

I was watching a tv show meanwhile .. but i managed to come up with this …

This script is showing a picture, when you press ESC it wil show some details.After that it will select another random picture.

Improvements : reading tags and other metadata from my photo database, to give more information.

#!/bin/bash
while true ; do
shuf -n 1 allpix > /tmp/randompix
pix=$(cat /tmp/randompix | cut -f2 -d\> | cut -f1 -d\<)
dir=$(cat /tmp/randompix | cut -f4 -d\> | cut -f1 -d\< |cut -f3 -d\; | sed 's#\\#/#g')
displaypath=/mnt/${dir}/${pix}
info=$(cat /tmp/randompix | rev | cut -f-4 -d\\ | rev | cut -f1 -d \<)
convert -background black -fill white  -size 1920x1080 -pointsize 24 label:"$info" info.jpg
pqiv -i --fullscreen "$displaypath"
pqiv -i --fullscreen info.jpg
done

Which gave me … this

Welllll .. it toke 20 minutes .. so i lost
(Must have been the wine)

Acdsee quick negative converting

Using a setup like this, i could digitize these negatives quite quickly.
But they’re are all beige. (And negative ofcourse)
I made a tutorial for people who need to digitize and convert non-raw black-white, color negatives, and negatives on these kind of beige film using windows.

I’m using a D7100 Nikon with a 70mm lens. The lightbox is a Haiser Slimlite Plano.

Setup your camera, focus one time, and put it on manual.
Cut from a piece of black paper a frame in which you keep the negatives.
(Taking a picture without, can result in weird inconsistent lighting, and will have effect on auto-leveling in Acdsee)

Take the pictures and move them to your machine.

Openup acdsee, select a picture and press CTRL-E

Crop and rotate first, else it wil have a effect on leveling!

Next press White Balance and select auto

After that press Special Effect and Negative

Still not okay

Next press Auto Levels .. and presto (Move the slider to your liking ..)
Note! .. changing the order of Cropping, White Balance, Effect and Auto Levels will affect the result!

This is probably all scriptable in Linux, but this is a Windows howto.

For conversions on linux i use Freds ImageMagick scripts:
( http://www.fmwconcepts.com/imagemagick/index.php )
autolevel autowhite neg2pos negative2positive and autotone

Photoshoots again

Today we did a photoshoot again. (Arja needed some pictures again). Nice to have some practice again.
I’ve been setting up a little studio in our shed. i’ve got a nice interchangeable background with multiple colors/fabrics. (White/Black/Chromagreen/Blue cloth)
I also installed the semi professional lights below.

Nice lightweight stands, with a lightbox, and a remote trigger system.
It has two modes. Flash and permanent lighting. It’s a nice piece of equipment, don’t get me wrong. But lately i’ve been using this!

These are led panels, meant for installing in suspended ceilings. I’ve got these from a friend.

I like these because they are white, and the other one a little yellow. And produce a lot of light!
Unlike the permanent lighting mode of the FalconEyes. (When using the permanent lighting mode you see what the lighting wil be. Unlike using a flash. (Which is also great, when going for other photographs)

When using a reflector like below, you can see the effect instantly.

We used the golden part to get a little warmth into the pictures.

I’m using my big painting-easel to hold the led panels. I just placed the picture holder upside down.

Those led panels are cheap, and you can get them in 3000k,4000k and 6000k (Kelvin) those three i’ve seen in online shops doing a quick search.

The rest of my equipment is for another post.

  • Nikon D750
  • Nikon D7100
  • Lenses: 60mm/35mm/200mm and 300mm