Pi NoIR Photos: Feeding Birds 29/04/2020
Posted on Apr 30th, 2020
Today we step into a lighter topic. I have adjusted the Pi NoIR camera for myself a touch. After the photos I will share what I made, and did, and talk about the slight trouble I have at the moment.
Please note: feel free to share these photos, they’re no-copyright, but it would be awesome to open up to more people.
These are just a few at random I chose, the entire collection of 308 photos can be downloaded here, 802mb. It will stay available for a few weeks.
Thoughts and Code
As you will note, I have messed up the blue filter, a Roscolux #2007 Storaro Blue, and currently cant use it. I noticed I could get it for around R500 for 20×24 inch. Not quite what I’m willing to spend money on.
So it would be nice to get ideas for how to get a new copy of that filter to use, that is why the hues are like this today. I feel bad about it, but it isn’t a problem that would mean I can never use the camera again.
So, before sharing my gimpy mount, here is the code. I adjusted it from open source code I found, no memory of where, but similar to this.
#!/usr/bin/python import os import os.path from os import path import io import subprocess import os import time from datetime import datetime from PIL import Image import shutil import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(22, GPIO.OUT) # red GPIO.setup(23, GPIO.OUT) # green # this is needed to reset awb every boot os.system('sudo vcdbg set awb_mode 0') time.sleep(5) # usb move files code def usb_callback(): print('usb_callback') path = '/home/pi/Desktop/_photos/' dest = '/cake' GPIO.output(22, True) while True: print(' - restart: sleep 5') try: time.sleep(5) num_files = 0 if os.path.isdir(path): num_files = len([f for f in os.listdir(path)if os.path.isfile(os.path.join(path, f))]) print(' - num files:') print(num_files) while num_files > 0: for r, d, f in os.walk('/media/pi/'): for folder in d: if os.path.isdir(os.path.join('/media/pi/', folder)): dest = os.path.join('/media/pi/', folder) print(dest) print(num_files) morefile = True if os.path.isdir(dest): print(' - dest is here' ) while num_files > 0: files = os.listdir(path) for f in files: shutil.move(path+f, dest) num_files = len([f for f in os.listdir(path)if os.path.isfile(os.path.join(path, f))]) num_files = len([f for f in os.listdir(path)if os.path.isfile(os.path.join(path, f))]) print(' - sleep 5') time.sleep(5) if os.path.isdir('/home/pi/Desktop/_photos'): print(' - rm dir') os.rmdir('/home/pi/Desktop/_photos') print('try makke complete file') GPIO.output(22, False) shutil.copy('/home/pi/Desktop/done', dest) time.sleep(5) print('bye bye') except Exception as e: print(e) time.sleep(60) # Motion detection settings: # Threshold - how much a pixel has to change by to be marked as "changed" # Sensitivity - how many changed pixels before capturing an image, needs to be higher if noisy view # ForceCapture - whether to force an image to be captured every forceCaptureTime seconds, values True or False # filepath - location of folder to save photos # filenamePrefix - string that prefixes the file name for easier identification of files. # diskSpaceToReserve - Delete oldest images to avoid filling disk. How much byte to keep free on disk. # cameraSettings - "" = no extra settings; "-hf" = Set horizontal flip of image; "-vf" = Set vertical flip; "-hf -vf" = both horizontal and vertical flip threshold = 50 sensitivity = 100 forceCapture = True forceCaptureTime = 60 * 60 # Once an hour filepath = "/home/pi/Desktop/_photos" filenamePrefix = "capture" diskSpaceToReserve = 40 * 1024 * 1024 # Keep 40 mb free on disk cameraSettings = "" # settings of the photos to save saveWidth = 3280 saveHeight = 2464 saveQuality = 100 # Set jpeg quality (0 to 100) # Test-Image settings testWidth = 100 testHeight = 75 # this is the default setting, if the whole image should be scanned for changed pixel testAreaCount = 1 testBorders = [ [[1,testWidth],[1,testHeight]] ] # [ [[start pixel on left side,end pixel on right side],[start pixel on top side,stop pixel on bottom side]] ] # testBorders are NOT zero-based, the first pixel is 1 and the last pixel is testWith or testHeight print('Preparing') # in debug mode, a file debug.bmp is written to disk with marked changed pixel an with marked border of scan-area # debug mode should only be turned on while testing the parameters above debugMode = False # False or True # Capture a small test image (for motion detection) def captureTestImage(settings, width, height): command = "raspistill %s -w %s -h %s -t 200 -e bmp -n -o -" % (settings, width, height) #imageData = StringIO.StringIO() imageData = io.BytesIO() imageData.write(subprocess.check_output(command, shell=True)) imageData.seek(0) im = Image.open(imageData) buffer = im.load() imageData.close() return im, buffer # Capture a small test image (for motion detection) def captureTestImage(settings, width, height): command = "raspistill %s -w %s -h %s -t 200 -e bmp -n -o -" % (settings, width, height) #imageData = StringIO.StringIO() imageData = io.BytesIO() imageData.write(subprocess.check_output(command, shell=True)) imageData.seek(0) im = Image.open(imageData) buffer = im.load() imageData.close() return im, buffer # Keep free space above given level def keepDiskSpaceFree(bytesToReserve): if (getFreeSpace() < bytesToReserve): sys.exit() # Get available disk space def getFreeSpace(): st = os.statvfs(filepath + "/") du = st.f_bavail * st.f_frsize return du # Save a full size image to disk def saveImage(settings, width, height, quality, diskSpaceToReserve, count): if (getFreeSpace() > diskSpaceToReserve): time = datetime.now() filename = filepath + "/" + filenamePrefix + "-%06d.a.jpg" % count subprocess.call("raspistill %s -w %s -h %s -t 200 -e jpg -q %s -n -o %s" % (settings, width, height, quality, filename), shell=True) print("Captured %s" % filename) def motion(): path = '/home/pi/Desktop/_photos/' count = len([f for f in os.listdir(path)if os.path.isfile(os.path.join(path, f))]) count = count + 1 # Get first image image1, buffer1 = captureTestImage(cameraSettings, testWidth, testHeight) # Reset last capture time lastCapture = time.time() while (True): # Get comparison image image2, buffer2 = captureTestImage(cameraSettings, testWidth, testHeight) # Count changed pixels changedPixels = 0 takePicture = False if (debugMode): # in debug mode, save a bitmap-file with marked changed pixels and with visible testarea-borders debugimage = Image.new("RGB",(testWidth, testHeight)) debugim = debugimage.load() for z in range(0, testAreaCount): # = xrange(0,1) with default-values = z will only have the value of 0 = only one scan-area = whole picture for x in range(testBorders[z]-1, testBorders[z]): # = xrange(0,100) with default-values for y in range(testBorders[z]-1, testBorders[z]): # = xrange(0,75) with default-values; testBorders are NOT zero-based, buffer1[x,y] are zero-based (0,0 is top left of image, testWidth-1,testHeight-1 is botton right) if (debugMode): debugim[x,y] = buffer2[x,y] if ((x == testBorders[z]-1) or (x == testBorders[z]-1) or (y == testBorders[z]-1) or (y == testBorders[z]-1)): # print "Border %s %s" % (x,y) debugim[x,y] = (0, 0, 255) # in debug mode, mark all border pixel to blue # Just check green channel as it's the highest quality channel pixdiff = abs(buffer1[x,y] - buffer2[x,y]) if pixdiff > threshold: changedPixels += 1 if (debugMode): debugim[x,y] = (0, 255, 0) # in debug mode, mark all changed pixel to green # Save an image if pixels changed if (changedPixels > sensitivity): takePicture = True # will shoot the photo later if ((debugMode == False) and (changedPixels > sensitivity)): break # break the y loop if ((debugMode == False) and (changedPixels > sensitivity)): break # break the x loop if ((debugMode == False) and (changedPixels > sensitivity)): break # break the z loop print(takePicture) if (debugMode): debugimage.save(filepath + "/debug.bmp") # save debug image as bmp print("debug.bmp saved, %s changed pixel" % changedPixels) # else: # print "%s changed pixel" % changedPixels # Check force capture if forceCapture: if time.time() - lastCapture > forceCaptureTime: takePicture = True if takePicture: lastCapture = time.time() saveImage(cameraSettings, saveWidth, saveHeight, saveQuality, diskSpaceToReserve, count) count = count + 1 # Swap comparison buffers image1 = image2 buffer1 = buffer2 if not path.exists('/home/pi/Desktop/_photos'): os.mkdir('/home/pi/Desktop/_photos') GPIO.output(23, True) motion() else: usb_callback()
So, my SD card has 6Gb free, so it makes a folder _photos on the desktop, then starts the camera for motion detection. When it boots, if it has the folder _photos it automatically starts to try moving them to an inserted media.
You will also notice I added two LEDs to indicate if it is working. A green flashing one for when it is taking photos, a red one for if it needs to copy and remove the folder. The red LED then turns off after the photos were all copied, the folder removed, and then it tries to make a file called ‘done’ on the flash drive.
I know the code needs several changes, such as slightly easier motion detection, I believe the scale is not quite adjusted to my liking. Also, the original code would just delete older photos when the SD card got too full, I didn’t like that. I don’t mind if I miss because photos stop coming through on something that happens later when we run out of dish space.
I also know certain imports are lazily added, though, so apologies for the duplicate ideas added. I will eventually clean the code a little, I just felt lazy today. I need to start bringing more content again, if at all possible.
The second thing to share is how it all looks for me now. The use, and so on. I essentially put effort into making it easier for my to safely mount it somewhere to use for the photos. The photos below are before the above.
So, I will make a cover for the camera mount I have. I built it with the idea that when I go to camping site in a game reserve with the animals able to come through the camp I can set it up in tons of places to get motion detection photographs of the wildlife.
Finally, since I mentioned wanting to sort the filters out again is what I need for the future, I figure I can point out my other plans.
I also want to make it a lid, so to speak. I want to cover it at the top so that if birds land on it they cant let their bowel make the system dirty. I also believe it could be harmful to the Pi itself.
I also figure I will make lens cover for where the camera sees out the mount. You will not in half the photos I shared it was too bright, I’m going to test with temporary solutions till I find the perfect fit.
I have the need for IR LEDs, which I will be picking up at some point, so that I can take a look at my night’s sleep cycle. I just felt like doing that.
I also have several small solar panels that I can get to around 6 to 9 volts, I will be making a solar charger for my rechargeable battery packs. Similarly, I want to make a meter for myself to read the voltage they’re on.
Any further thoughts are always welcome. I’m having tons of fun with the Pi NoIR camera already, the reason I love to take steps further in what I try out and make.