Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

iPhoneのカメラで写真撮影から現像までの技術を紐解く

satoshin21
September 07, 2019

 iPhoneのカメラで写真撮影から現像までの技術を紐解く

https://fortee.jp/iosdc-japan-2019/proposal/a2e9b461-2c0d-47c5-b3cc-cef012e3f260

2019/09/07 早稲田大学 西早稲田キャンパス
Track B 13:30〜

iPhoneやスマートフォンにおける「カメラ」という機能。
写真を撮る、という日常的に行われている行動ではありますが、その実撮影という機能を実現するために様々な技術的処理が使われています。

このセッションでは、「カメラが光を取り込む仕組み」から「iOSで取り込んだデータを画像化する」という所を深掘りし
写真撮影という技術を皆さんと一緒に紐解いていければと思います。

satoshin21

September 07, 2019
Tweet

More Decks by satoshin21

Other Decks in Technology

Transcript

  1. whoami - @satoshin21 - iOS Application Developer at Eureka, Inc.

    ‧Pairs JP - Bitrise User Group Meetup Organizer - Camera ‧α7M3
  2. ⚠ iOSの話は1/3ぐらいしかでてきません ⚠ - 今回はカメラ技術について話します - 後半はMetalを使ったiOSでマニュアルカメラアプリの作り ⽅を紹介しますが、iOSの話が少ない - けど、どうしてもカメラの話がしたい!!

    ‧今後ARやDepthの技術が進んでいても切り離せないハード ウェアの素晴らしさ、問題、課題 ‧単純に光学技術って⾯⽩い。カメラはエンジニアと相性 最⾼
  3. カメラの歴史 - カメラ‧オブスクラ - 紀元前350年頃 - Camera(部屋) Obscra(暗い) - 物体に光が当たると特定の

    ⾊の波⻑を乱反射(錯乱)す る事で物が⾒える - 光の直進性を利⽤して壁に 逆転した倒⽴像を映し出す https://kimuko.net/wpress/engawa/2015/09/13/写真家とつくる「カメラ·オブスキュラ」って?/
  4. ⾊収差 - 軸上⾊収差 - 凸レンズに光を当てた時に発⽣する⾊波⻑によって屈折率 が異なる事に起因する - ⾚い⾊の焦点は遠くに、⻘い⾊の焦点は近くになる - ⼀枚のフィルム、撮像素⼦に写す際ににじみが発⽣する

    - この焦点距離の差が⼤きいレンズは分散が⼤きいと⾔われ る - 分散の度合いを表す値にアッペ数があり、単位は「ν」 - アッペ数が50以下をフリントガラス、50以上をクラウンガ ラスと呼ぶ
  5. 銀塩カメラ‧フィルムの時代 - フィルムには感光性のあるハ ロゲン化銀が塗布されている - ハロゲン化銀に光を当てると 化学変化して感光核が作ら れ、フィルムに画像パターン が作られる -

    現像液に付けると感光核の周 囲が銀粒⼦に変化 = 現像 - ハロゲン化銀に⾊素を加える と特定の⾊に感光する https://global.canon/ja/technology/s_labo/light/003/01.html
  6. 撮像素⼦ - サイズ - iPhone Xsなどのデュアルカメラにはそれぞれ別のサイズの 撮像素⼦ ‧広⾓側 1/2.5型 ‧望遠側

    1/3.6型 - サイズ表記は真空管(撮像管 直径約1インチ)を使っていた 時の名残 https://ja.wikipedia.org/wiki/撮像管
  7. Sony Exmor IMX333 - おそらくiPhone Xsで採⽤されている裏⾯照射式イメージセ ンサー - 1.4μmの画素(Pixel3などと同じ) -

    裏⾯照射式とは、フォトダイオードと配線部分を裏返し、 よりフォトダイオードへの光を受けやすくした構造 オンチップレンズ 配線部 カラーフィルタ フォトダイオード
  8. 絞りと被写界深度 絞り⽻根 - 絞りは絞る事で被写界深度を深く == より⼤きな範囲にピ ントを合わせられる - また、収差が発⽣しやすいレンズの外側部分を使わないた め、収差の少ないよりキレイな写真を撮ることができる

    - iPhoneの絞りは固定で広⾓側F1.8と望遠側F2.4(iPhone Xs) の異なるF値を持つカメラを備えており、その差分を合成し てポートレートモードを実現している
  9. AVCaptureDevice let deviceDiscoverySession = AVCaptureDevice.DiscoverySession( deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position:

    .back) // ϓϩύςΟͷ৚݅Λຬͨͨ͠ΧϝϥσόΠεͷऔಘ let devices = deviceDiscoverySession.devices let device = devices.first(where: { $0.deviceType == .builtInWideAngleCamera })
  10. AVCaptureDevice let deviceDiscoverySession = AVCaptureDevice.DiscoverySession( deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position:

    .back) // ϓϩύςΟͷ৚݅Λຬͨͨ͠ΧϝϥσόΠεͷऔಘ let devices = deviceDiscoverySession.devices let device = devices.first(where: { $0.deviceType == .builtInWideAngleCamera })
  11. add CaptureDevice as Input let captureSession = AVCaptureSession() captureSession.sessionPreset =

    .photo let input = try AVCaptureDeviceInput(device: device) captureSession.addInput(wideInput)
  12. AVCaptureVideoPreviewLayer final class PreviewView: UIView { var previewLayer: AVCaptureVideoPreviewLayer {

    return self.layer as! AVCaptureVideoPreviewLayer } func set(session: AVCaptureSession) { previewLayer.session = session } override public class var layerClass: Swift.AnyClass { get { return AVCaptureVideoPreviewLayer.self } } }
  13. iOS & RAW - iOS10からRAW(DNG)のキャプチャが可能に - DNG = Digital Negative

    ‧Adobe Systemsの開発したRAWファイル形式 ‧Open Source
  14. iOSでRAWをキャプチャするには - Appleのドキュメント「Capturing Photos in RAW Format」 がわかりやすい - 以下、ドキュメントの内容をかいつまんで説明します

    https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/ capturing_still_and_live_photos/capturing_photos_in_raw_format
  15. シャッター @IBAction func shutter(_ sender: ShutterButton) { guard let availableRawFormat

    = photoOutput.availableRawPhotoPixelFormatTypes.first else { return } let photoSettings = AVCapturePhotoSettings( rawPixelFormatType: availableRawFormat, processedFormat: [AVVideoCodecKey : AVVideoCodecType.jpeg]) photoSettings.isAutoStillImageStabilizationEnabled = false photoOutput.capturePhoto( with: photoSettings, delegate: captureProcessor ) }
  16. シャッター @IBAction func shutter(_ sender: ShutterButton) { guard let availableRawFormat

    = photoOutput.availableRawPhotoPixelFormatTypes.first else { return } let photoSettings = AVCapturePhotoSettings( rawPixelFormatType: availableRawFormat, processedFormat: [AVVideoCodecKey : AVVideoCodecType.jpeg]) photoSettings.isAutoStillImageStabilizationEnabled = false photoOutput.capturePhoto( with: photoSettings, delegate: captureProcessor ) }
  17. RAWImageProcessor class RAWCaptureProcessor: NSObject, AVCapturePhotoCaptureDelegate { func photoOutput(_ output: AVCapturePhotoOutput,

    didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { if photo.isRawPhoto { // Save the RAW (DNG) file data to a URL. let dngFileURL = self.makeUniqueTempFileURL(extension: "dng") do { try photo.fileDataRepresentation()!.write(to: dngFileURL) rawImageFileURL = dngFileURL } catch { fatalError("couldn't write DNG file to URL") } } else { self.compressedFileData = photo.fileDataRepresentation()! } }
  18. RAWImageProcessor class RAWCaptureProcessor: NSObject, AVCapturePhotoCaptureDelegate { func photoOutput(_ output: AVCapturePhotoOutput,

    didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { if photo.isRawPhoto { // Save the RAW (DNG) file data to a URL. let dngFileURL = self.makeUniqueTempFileURL(extension: "dng") do { try photo.fileDataRepresentation()!.write(to: dngFileURL) rawImageFileURL = dngFileURL } catch { fatalError("couldn't write DNG file to URL") } } else { self.compressedFileData = photo.fileDataRepresentation()! } }
  19. シャッタースピード // CMTime(value: 24, timescale: 1000000) = 24/1000000, 1/42000 captureDevice.activeFormat.minExposureDuration

    // CMTime(value: 1, timescale: 1) = 1s captureDevice.activeFormat.maxExposureDuration try? captureDevice.lockForConfiguration() captureDevice.setExposureModeCustom( duration: .init(value: 1, timescale: 1000), iso: captureDevice.iso, completionHandler: nil) captureDevice.unlockForConfiguration()
  20. Appleの提供するRAW現像サンプルコード - RawExpose: Using CIRAWFilter to Decode RAW Images -

    CIFilter & CIRAWFilter API - OpenGL(GLKView) ‧Deprecated in iOS 12 - MetalKitを使って書き直す https://developer.apple.com/library/archive/samplecode/RawExpose/Introduction/Intro.html#//apple_ref/doc/uid/TP40017310
  21. RAW Processing Flow - CoreImage & MetalでDNGを表⽰ ‧CIFilterをDNGファイルで初期化 ‧出⼒されたCIImageをMTKViewにレンダリング -

    CIFilterをCIRAWFilterOptionで編集 https://developer.apple.com/library/archive/samplecode/RawExpose/Introduction/Intro.html#//apple_ref/doc/uid/TP40017310
  22. Metal周り初期化 private let device: MTLDevice private let context: CIContext private

    let commandQueue: MTLCommandQueue required init?(coder aDecoder: NSCoder) { device = MTLCreateSystemDefaultDevice()! context = CIContext(mtlDevice: device) commandQueue = device.makeCommandQueue()! }
  23. MTKViewの初期化 @IBOutlet weak var mtkView: MTKView override func viewDidLoad() {

    super.viewDidLoad() mtkView.framebufferOnly = false mtkView.delegate = self mtkView.enableSetNeedsDisplay = true mtkView.device = device }
  24. Metal Rendering 1 extension ViewController: MTKViewDelegate { func draw(in view:

    MTKView) { guard let drawable = view.currentDrawable, let extentSize = extentSize else { return } guard let outputImage = ciRawFilter?.outputImage else { return } guard let commandBuffer = commandQueue.makeCommandBuffer() else { return } //...
  25. Metal Rendering 2 extension DetailViewController: MTKViewDelegate { func draw(in view:

    MTKView) { //… let colorSpace = CGColorSpaceCreateDeviceRGB() context.render(outputImage, to: drawable.texture, commandBuffer: commandBuffer, bounds: imageRect, colorSpace: colorSpace) commandBuffer.present(drawable) commandBuffer.commit() } }
  26. 露光量とは - 画像全体の明るさを⽰す値 - 露光量をEV、絞り値をN, シャッタースピードをtとし て以下の計算式で算出する - - 絞り値が2,

    SSが4の場合は露 光量は0 - baselineExposureはこの値を 調整する EV = log2 N2 − log2 t https://ja.wikipedia.org/wiki/露出(写真)
  27. END