<jupyter_start><jupyter_text>Vogelkamera mit dem Raspberry PI <NAME>, Juni 2016  Wie alles begann... ... wird die überhaupt benutzt?  Erster Versuch: Digitalkamera* Intervallaufnahmen mit Digitalkamera* Suchen nach "interessanten" Bildern mit Python:<jupyter_code>import glob
import numpy
from matplotlib import image

def rgb2gray(rgb):
    return numpy.dot(rgb[...,:3], [0.299, 0.587, 0.144])

oldimg = None

for infile in glob.glob('*.JPG'):
    img = rgb2gray(image.imread(infile))

    if oldimg is not None:
        diff = numpy.linalg.norm(img - oldimg)
        # ... do something
    oldimg = img<jupyter_output><empty_output><jupyter_text>Probleme:* Batterie der Kamera reicht nur für gut drei Stunden* Bilder kopieren und auswerten per Hand wird nervig...  Zweiter Versuch: Raspberry PI und Kamera-Modul  Sonnenstandsberechnung mit astral<jupyter_code>from astral import Astral

a = Astral()
a.solar_depression = 3
location = a['Berlin']

location.latitude = 50.9534001
location.longitude = 6.9548886
location.elevation = 56

print(location.dawn())
print(location.dusk())<jupyter_output>2015-06-09 05:00:25+02:00
2015-06-09 22:02:27+02:00<jupyter_text>PICamera — Python-Modul für die Kamera<jupyter_code>import time
import picamera

with picamera.PiCamera() as camera:
    camera.resolution = (1024, 768)
    camera.start_preview()
    # Camera warm-up time
    time.sleep(2)
    camera.capture('test.png')

    camera.start_recording('my_video.h264', motion_output='motion.data')
    camera.wait_recording(60)
    camera.stop_recording()<jupyter_output><empty_output><jupyter_text>Bewegungsdaten* Kamerachip liefert Bewegungsdaten zur Kodierung mit H264-Codec* Es werden 16x16-Pixel-Blöcke betrachtet* Für jeden Block: 2D-Vektor, wohin sich der Block bewegt + Wert, wie sehr sich alter und neuer Block unterscheiden  Testen :)  Bewegungsdaten on the fly analysieren<jupyter_code>class MotionAnalyser(picamera.array.PiMotionAnalysis):

    FRAMES = 5

    def __init__(self, *args, **kwargs):
        super(MotionAnalyser, self).__init__(*args, **kwargs)
        self.motion = None
        self.last_motions = deque([0] * self.FRAMES, maxlen=self.FRAMES)

    def analyse(self, m):
        data = numpy.sqrt(
            numpy.square(m['x'].astype(numpy.float)) +
            numpy.square(m['y'].astype(numpy.float))
        )
        norm = numpy.linalg.norm(data)
        self.last_motions.append(norm)
        if min(self.last_motions) > MOTION_THRESHOLD:
            self.motion = True

with MotionAnalyser(camera) as analyser:
    camera.start_recording('/dev/null', format='h264', motion_output=analyser)
    while True:
        if analyser.motion:
            camera.stop_recording()
            # ...
            break
        time.sleep(0.1)<jupyter_output><empty_output><jupyter_text>Erste Ergebnisse nach ein paar warmen, trockenen Tagen: Es kommen Vögel zum Trinken und Baden! Aber...  Wellen durch Wind und Regen = Bewegung :(  Dritter Versuch: Passiver Infrarot-Sensor   Das RPi-GPIO-Modul<jupyter_code>from RPi import GPIO
import time

IR_PIN = 14 # data

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(IR_PIN, GPIO.IN)

try:
    while True:
        if GPIO.input(IR_PIN):
            print('Bewegung!')
        time.sleep(1)
except KeyboardInterrupt:
    GPIO.cleanup()<jupyter_output><empty_output><jupyter_text>Das RPi-GPIO-Modul<jupyter_code>GPIO.add_event_detect(IR_PIN, GPIO.RISING)
while True:
    if GPIO.event_detected(IR_PIN):
        print('Bewegung!')
    time.sleep(1)<jupyter_output><empty_output><jupyter_text>Interrupts:<jupyter_code>def my_callback(channel):
    print('Bewegung!')

GPIO.add_event_detect(IR_PIN, GPIO.RISING, callback=my_callback)<jupyter_output><empty_output><jupyter_text>Aber...* Sensor nicht empfindlich genug für kleine Vögel* Sensor reagiert träge:(Gibt es bessere Sensoren?  Vierter Versuch: Bewegungsanalyse v. 2* Idee: Ignorieren der Wasseroberfläche* Vögel werden zumindestens beim Anflug erkannt und wenn sie auf dem Rand sitzen* Eventuell weniger Aufnahmen von badenden Vögeln in der Mitte der Tränke* Dafür kein Bilder-Spam bei Wind und Regen* Annahme: Kamera immer in gleicher Position, d.h. Wasseroberfläche anhand fester Pixelbereiche identifizierbar  Motion Mask  Anpassen der Bewegungs-Analyse<jupyter_code>motion_mask = matplotlib.image.imread('motion_mask.png')[..., 0]

class MotionAnalyser(picamera.array.PiMotionAnalysis):

    # ...
    
    def analyse(self, m):
        data = numpy.sqrt(
            numpy.square(m['x'].astype(numpy.float)) +
            numpy.square(m['y'].astype(numpy.float))
        )
        data = numpy.multiply(data, motion_mask)
        norm = numpy.linalg.norm(data)
        self.last_motions.append(norm)
        if min(self.last_motions) > MOTION_THRESHOLD:
            self.motion = True<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10796862097440132, 'fraction_numerical': 0.02250206440957886, 'mean_word_length': 3.989701338825953, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '126613', 'license': 'MIT', 'n_tokens_mistral': 1817, 'n_tokens_neox': 1726, 'n_words': 366, 'path': 'talk/Vogelkamera.ipynb', 'repo_name': 'rbreu/birdcam', 'size': '4850'}