編集履歴一覧に戻る
airpocketのアイコン画像

airpocket が 2022年07月01日19時07分17秒 に編集

初版

タイトルの変更

+

100年カメラは旧世紀の夢を見るか

タグの変更

+

RaspberryPi

+

Python

+

opencv

+

RaspberryPiZero2W

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

製作品

ライセンスの変更

+

(MIT) The MIT License

本文の変更

+

# 1.はじめに 前回作成した8mmフィルムカメラのデジタル化では70年前のカメラを使用していました。キリの良い100年前という言葉を使いたかったので100年前のカメラもデジタル化してみました。 ![キャプションを入力できます](https://camo.elchika.com/f192c7f9e31a365f949a78912f586d6736299d86/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f66613837363931312d613266352d346438342d626162662d316232303965666139346332/) # 2.使用したカメラ 使用したのは、"Kodak No. 2 Autographic Brownie"と言うカメラです。使用するフィルムは60mm幅のいわゆる中判カメラです。当時、自社のフィルムの拡販を狙ってKodak社が販売している廉価版のカメラで、程度にこだわらなければ今でも数千円程度で手に入ります。 ![キャプションを入力できます](https://camo.elchika.com/1ab9a58b7dc3a21992f248703f24e5a372a56e61/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f35373061366130662d376132622d346636312d626334322d623838663133383461376538/) # 3.改造方法 このカメラのイメージプレーンは60×40mmと非常に大きいため、同等のフォーマットのイメージセンサが出に入りません。そこで、レンズから入った光を一度スクリーンに投影し、その像をデジタルイメージセンサで撮影します。この方式は[からあげさんの例のカメラ](https://protopedia.net/prototype/2834)を参考にさせていただきました。 ![キャプションを入力できます](https://camo.elchika.com/e26ab2754912d4be11613f9e58c8722b183f0341/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f36653666666163312d616563622d346435322d613064342d336539653932646639666564/) # 4.スクリーン投影のテスト スクリーンにはAliexpress で購入した透過型プロジェクタ用のスクリーンサンプルを使用しています。A4サイズのスクリーンが5種類入っていて検討しやすかったです。 @[youtube](https://www.youtube.com/watch?v=e6QZ6KJgcEs) 晴れた日の屋外であれば十分な明るさの像を得ることができます。 # 5.筐体の制作 本来のカメラは、蛇腹レンズ部とフィルムを収納するボディの2分割構造となっています。今回はボディ部を3Dプリンタで作り直し、RaspiCameraを設置する暗箱部を制作しました。カメラを制御するRaspberryPiZero2Wを載せるブラケットも3Dプリンタで制作しています。 ![キャプションを入力できます](https://camo.elchika.com/dfd5f5d7d68619909e080a99b70bd2d703fcca4d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f33363061393966392d623436312d343532372d386661652d376565303236363463646163/) # 6.撮影できたもの このカメラで撮影するとこのような写真が撮れました。周囲が暗くなるのは周辺減光とスクリーン投影時の光の拡散による影響です。 ![キャプションを入力できます](https://camo.elchika.com/1548c23cf467f74e140fa7f68d726c8f34a71fe8/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f31383264333033662d303730312d346630382d386437382d613735663932626264376330/) 減光による影響を測定して補正するプログラムも検討してみましたが、あまり面白くなかったのでこのカメラの味として許容することにしました。 ![周辺減光による光量変化の可視化](https://camo.elchika.com/2fbf38ee4588391ec5146cd30185957dcba388e7/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f36633833666535632d653765622d343037612d623138622d313433323864616465653961/) ![補正前写真](https://camo.elchika.com/2c531802f7a6faaf5f39ce0a6d74152fa9242181/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f62313032323434632d303233642d343835612d613434322d316435633039343538303163/) ![補正後写真](https://camo.elchika.com/41e7bed6945d0745875b06426e998699c9f13ba7/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38666662353561372d373433312d346234382d383832642d3565323665663534343465652f61363561643933392d626630642d343837612d383233322d663164333664633537343836/) ※補正後写真で黒ブチが出ているのは光軸ずれも補正したため。 # 7.プログラム 今回作成したプログラムです。開発中のため、PCと接続した状態でキーボード入力によりシャッター、シャッタースピード変更を行います。 OSはRaspbery pi os bullseye 32bit liteを使用しています。 事前にpython3やopencv-pythonのインストールが必要です。 ```python import cv2 import numpy as np import os import termios, tty, sys import datetime # Set the number of pixels based on a 4:3 image size. WIDTH = 320 HEIGHT = 240 magnification = 4 WIDTH *= magnification HEIGHT *= magnification # The coefficient for adjusting vignetting. The optimum value varies dependig on the brightness of the image and other factors gainFactor = 1 #default = 0.8 distanceFactor = 0.7 #default = 0.71 cosIndex = 4 #default = 4 # Parameters for correcting the optical axis of the lens and image sensor of a film camera offsetRight = 100 * magnification offsetDown = 100 * magnification # The number of exposures for multiple exposures. overlay = 1 # Exposure time exposureTime = 100 # Correct for vignetting? gainFlag = False # Correct optical axis? opticalAxisOffset = False capture = cv2.VideoCapture(0) capture.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH) capture.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT) capture.set(cv2.CAP_PROP_FPS,1.0) capture.set(cv2.CAP_PROP_BUFFERSIZE, 1) os.system('v4l2-ctl -d /dev/video0 -c auto_exposure=1') # 0:auto 1:manual os.system('v4l2-ctl -d /dev/video0 -c exposure_time_absolute=' + str(exposureTime)) # 1to10000 os.system('v4l2-ctl -d /dev/video0 -c exposure_metering_mode=0') # If you need to set more conditions, you may use the following commdands. #os.system('v4l2-ctl -d /dev/video0 -c exposure_dynamic_framerate=1') #os.system('v4l2-ctl -d /dev/video0 -c iso_sensitivity_auto=0') # 0:manual 1:auto #os.system('v4l2-ctl -d /dev/video0 -c iso_sensitivity=0') # 0,1,2,3,4 #os.system('v4l2-ctl -d /dev/video0 -c auto_exposure_bias=0') #-24 to 24 #os.system('v4l2-ctl -d /dev/video0 -c scene_mode=8') #os.system('v4l2-ctl --set-ctrl auto_exposure=1,exposure_time_absolute=10000,auto_exposure_bias=12,iso_sensitivity=4') def timeNow(): now = datetime.datetime.now() now = (str(now.year) + str(now.month).zfill(2) + str(now.day).zfill(2) + str(now.hour).zfill(2) + str(now.minute).zfill(2) + str(now.second).zfill(2)) print(now) return now def getch(): fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) tty.setcbreak(fd) ch = sys.stdin.read(1) termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch def takePicture(): global overlay, gainFlag, opticalAxisOffset, offsetRight, offsetDown, pictureNum for i in range(overlay): print("number of shot = ",i) ret, frame = capture.read() ret, frame = capture.read() if ret is False: print("cap error") if i == 0: frameO = frame/overlay else: frameO = frameO + frame/overlay if opticalAxisOffset == False: offsetRight = 0 offsetDown = 0 if gainFlag == True: h, w = frameO.shape[:2] size = max([h, w]) x = np.linspace((-w + offsetRight)/size, (w + offsetRight)/size, w) y = np.linspace((-h + offsetDown )/size, (h + offsetDown )/size, h) xx, yy = np.meshgrid(x, y) r = np.sqrt(xx**2 + yy**2) gain = 1 / np.power(np.cos(r * distanceFactor), cosIndex) * gainFactor gainmap = np.dstack([gain, gain, gain]) # frame = frame**2.2 frameO = np.clip(frameO * gainmap, 0., 255.0) # frame = frame**(1/2.2) now = timeNow() cv2.imwrite(now + ".jpg", frameO) print("push [s] to take pic / [q] to quit") if capture.isOpened() is False: raise IOError print("ready") print("push [s] to take pic / [q] to quit") while True: key = getch() if key == "q": break elif key == "s": print("shutter!") takePicture() capture.release() ``` # 8.stlファイル 筐体の3Dデータです。 [githubにて公開しています。](https://github.com/airpocket-soundman/100yearCamera) ※ちょっと整理してからアップします。 # 9.まとめ フィルムカメラのデジタル化はこれで2台目になりますが、スクリーン投影方式はかなり気楽に制作できるのがメリットです。 静止画だとRaspberry Pi Zeroでも十分な処理能力があり、かつ省電力なので電池駆動化も容易です。 最近はフィルムカメラのブームと言われつつも、中古市場には安価なフィルムカメラが山ほど滞留しています。絶好のハック対象ではないでしょうか。