Upgrade to Pro — share decks privately, control downloads, hide ads and more …

クロマキー合成を使い透過動画をAR空間に表示する

satoshi0212
September 06, 2019

 クロマキー合成を使い透過動画をAR空間に表示する

satoshi0212

September 06, 2019
Tweet

More Decks by satoshi0212

Other Decks in Technology

Transcript

  1. !12

  2. func createVideoNode(size: CGFloat, videoUrl: URL) -> SCNNode { // αΠζ͕খ͍͞ͱϏσΦͷղ૾౓͕མͪΔ

    let skSceneSize = CGSize(width: 1024, height: 1024) // AVPlayerੜ੒ let avPlayer = AVPlayer(url: videoUrl) // SKVideoNodeੜ੒ let skVideoNode = SKVideoNode(avPlayer: avPlayer) skVideoNode.position = CGPoint(x: skSceneSize.width / 2.0, y: skSceneSize.height / 2.0) skVideoNode.size = skSceneSize skVideoNode.yScale = -1.0 // ࠲ඪܥΛ্Լٯʹ͢Δ skVideoNode.play() // SKSceneੜ੒ let skScene = SKScene(size: skSceneSize) skScene.addChild(skVideoNode) // SCNMaterialੜ੒ let material = SCNMaterial() material.diffuse.contents = skScene material.isDoubleSided = true // SCNNodeੜ੒ let node = SCNNode() // SCNPlane(=SCNGeometryΛܧঝͨ͠Ϋϥε)ੜ੒ node.geometry = SCNPlane(width: size, height: size) node.geometry?.materials = [material] node.scale = SCNVector3(1, 0.5625, 1) return node } !24 "71MBZFS 4,7JEFP/PEF 4,4DFOF 4$/.BUFSJBM 4$/(FPNFUSZ 4$//PEF
  3. func createVideoNode(size: CGFloat, videoUrl: URL) -> SCNNode { // αΠζ͕খ͍͞ͱϏσΦͷղ૾౓͕མͪΔ

    let skSceneSize = CGSize(width: 1024, height: 1024) // AVPlayerੜ੒ let avPlayer = AVPlayer(url: videoUrl) // SKVideoNodeੜ੒ let skVideoNode = SKVideoNode(avPlayer: avPlayer) skVideoNode.position = CGPoint(x: skSceneSize.width / 2.0, y: skSceneSize.height / 2.0) skVideoNode.size = skSceneSize skVideoNode.yScale = -1.0 // ࠲ඪܥΛ্Լٯʹ͢Δ skVideoNode.play() // SKSceneੜ੒ let skScene = SKScene(size: skSceneSize) skScene.addChild(skVideoNode) // SCNMaterialੜ੒ let material = SCNMaterial() material.diffuse.contents = skScene material.isDoubleSided = true // SCNNodeੜ੒ let node = SCNNode() // SCNPlane(=SCNGeometryΛܧঝͨ͠Ϋϥε)ੜ੒ node.geometry = SCNPlane(width: size, height: size) node.geometry?.materials = [material] node.scale = SCNVector3(1, 0.5625, 1) return node } !25 "71MBZFS 4,7JEFP/PEF 4,4DFOF 4$/.BUFSJBM 4$/(FPNFUSZ 4$//PEF
  4. func createVideoNode(size: CGFloat, videoUrl: URL) -> SCNNode { // αΠζ͕খ͍͞ͱϏσΦͷղ૾౓͕མͪΔ

    let skSceneSize = CGSize(width: 1024, height: 1024) // AVPlayerੜ੒ let avPlayer = AVPlayer(url: videoUrl) // SKVideoNodeੜ੒ let skVideoNode = SKVideoNode(avPlayer: avPlayer) skVideoNode.position = CGPoint(x: skSceneSize.width / 2.0, y: skSceneSize.height / 2.0) skVideoNode.size = skSceneSize skVideoNode.yScale = -1.0 // ࠲ඪܥΛ্Լٯʹ͢Δ skVideoNode.play() // SKSceneੜ੒ let skScene = SKScene(size: skSceneSize) skScene.addChild(skVideoNode) // SCNMaterialੜ੒ let material = SCNMaterial() material.diffuse.contents = skScene material.isDoubleSided = true // SCNNodeੜ੒ let node = SCNNode() // SCNPlane(=SCNGeometryΛܧঝͨ͠Ϋϥε)ੜ੒ node.geometry = SCNPlane(width: size, height: size) node.geometry?.materials = [material] node.scale = SCNVector3(1, 0.5625, 1) return node } !26 "71MBZFS 4,7JEFP/PEF 4,4DFOF 4$/.BUFSJBM 4$/(FPNFUSZ 4$//PEF
  5. func createVideoNode(size: CGFloat, videoUrl: URL) -> SCNNode { // αΠζ͕খ͍͞ͱϏσΦͷղ૾౓͕མͪΔ

    let skSceneSize = CGSize(width: 1024, height: 1024) // AVPlayerੜ੒ let avPlayer = AVPlayer(url: videoUrl) // SKVideoNodeੜ੒ let skVideoNode = SKVideoNode(avPlayer: avPlayer) skVideoNode.position = CGPoint(x: skSceneSize.width / 2.0, y: skSceneSize.height / 2.0) skVideoNode.size = skSceneSize skVideoNode.yScale = -1.0 // ࠲ඪܥΛ্Լٯʹ͢Δ skVideoNode.play() // SKSceneੜ੒ let skScene = SKScene(size: skSceneSize) skScene.addChild(skVideoNode) // SCNMaterialੜ੒ let material = SCNMaterial() material.diffuse.contents = skScene material.isDoubleSided = true // SCNNodeੜ੒ let node = SCNNode() // SCNPlane(=SCNGeometryΛܧঝͨ͠Ϋϥε)ੜ੒ node.geometry = SCNPlane(width: size, height: size) node.geometry?.materials = [material] node.scale = SCNVector3(1, 0.5625, 1) return node } !27 "71MBZFS 4,7JEFP/PEF 4,4DFOF 4$/.BUFSJBM 4$/(FPNFUSZ 4$//PEF
  6. func createVideoNode(size: CGFloat, videoUrl: URL) -> SCNNode { // αΠζ͕খ͍͞ͱϏσΦͷղ૾౓͕མͪΔ

    let skSceneSize = CGSize(width: 1024, height: 1024) // AVPlayerੜ੒ let avPlayer = AVPlayer(url: videoUrl) // SKVideoNodeੜ੒ let skVideoNode = SKVideoNode(avPlayer: avPlayer) skVideoNode.position = CGPoint(x: skSceneSize.width / 2.0, y: skSceneSize.height / 2.0) skVideoNode.size = skSceneSize skVideoNode.yScale = -1.0 // ࠲ඪܥΛ্Լٯʹ͢Δ skVideoNode.play() // SKSceneੜ੒ let skScene = SKScene(size: skSceneSize) skScene.addChild(skVideoNode) // SCNMaterialੜ੒ let material = SCNMaterial() material.diffuse.contents = skScene material.isDoubleSided = true // SCNNodeੜ੒ let node = SCNNode() // SCNPlane(=SCNGeometryΛܧঝͨ͠Ϋϥε)ੜ੒ node.geometry = SCNPlane(width: size, height: size) node.geometry?.materials = [material] node.scale = SCNVector3(1, 0.5625, 1) return node } !28 "71MBZFS 4,7JEFP/PEF 4,4DFOF 4$/.BUFSJBM 4$/(FPNFUSZ 4$//PEF
  7. func createVideoNode(size: CGFloat, videoUrl: URL) -> SCNNode { // αΠζ͕খ͍͞ͱϏσΦͷղ૾౓͕མͪΔ

    let skSceneSize = CGSize(width: 1024, height: 1024) // AVPlayerੜ੒ let avPlayer = AVPlayer(url: videoUrl) // SKVideoNodeੜ੒ let skVideoNode = SKVideoNode(avPlayer: avPlayer) skVideoNode.position = CGPoint(x: skSceneSize.width / 2.0, y: skSceneSize.height / 2.0) skVideoNode.size = skSceneSize skVideoNode.yScale = -1.0 // ࠲ඪܥΛ্Լٯʹ͢Δ skVideoNode.play() // SKSceneੜ੒ let skScene = SKScene(size: skSceneSize) skScene.addChild(skVideoNode) // SCNMaterialੜ੒ let material = SCNMaterial() material.diffuse.contents = skScene material.isDoubleSided = true // SCNNodeੜ੒ let node = SCNNode() // SCNPlane(=SCNGeometryΛܧঝͨ͠Ϋϥε)ੜ੒ node.geometry = SCNPlane(width: size, height: size) node.geometry?.materials = [material] node.scale = SCNVector3(1, 0.5625, 1) return node } !29 "71MBZFS 4,7JEFP/PEF 4,4DFOF 4$/.BUFSJBM 4$/(FPNFUSZ 4$//PEF
  8. override func viewDidLoad() { super.viewDidLoad() sceneView.delegate = self sceneView.scene =

    SCNScene() let videoUrl = Bundle.main.url(forResource: "video001", withExtension: "mp4")! let videoNode = createVideoNode(size: 1, videoUrl: videoUrl) videoNode.position = SCNVector3(0, 0, -3.0) sceneView.scene.rootNode.addChildNode(videoNode) } !30
  9. !31

  10. !32

  11. override func draw(_ dirtyRect: CGRect) { guard let device =

    device, let drawable = currentDrawable, let tempDrawable = bufferMtkView.currentDrawable, let image = makeCurrentVideoImage() else { return } ciContext.render(image, to: tempDrawable.texture, commandBuffer: nil, bounds: bounds, colorSpace: colorSpace) colorPixelFormat = tempDrawable.texture.pixelFormat let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeComputeCommandEncoder()! commandEncoder.setComputePipelineState(pipelineState) commandEncoder.setTexture(tempDrawable.texture, index: 0) commandEncoder.setTexture(drawable.texture, index: 1) let factors: [Float] = [ 0, // red 1, // green 0, // blue 0.43, // threshold 0.11 // smoothing ] for i in 0..<factors.count { var factor = factors[i] let size = max(MemoryLayout<Float>.size, 16) let buffer = device.makeBuffer( bytes: &factor, length: size, options: [.storageModeShared] ) commandEncoder.setBuffer(buffer, offset: 0, index: i) } commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup) commandEncoder.endEncoding() commandBuffer.present(drawable) commandBuffer.commit() } !40
  12. override func draw(_ dirtyRect: CGRect) { guard let device =

    device, let drawable = currentDrawable, let tempDrawable = bufferMtkView.currentDrawable, let image = makeCurrentVideoImage() else { return } ciContext.render(image, to: tempDrawable.texture, commandBuffer: nil, bounds: bounds, colorSpace: colorSpace) colorPixelFormat = tempDrawable.texture.pixelFormat let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeComputeCommandEncoder()! commandEncoder.setComputePipelineState(pipelineState) commandEncoder.setTexture(tempDrawable.texture, index: 0) commandEncoder.setTexture(drawable.texture, index: 1) let factors: [Float] = [ 0, // red 1, // green 0, // blue 0.43, // threshold 0.11 // smoothing ] for i in 0..<factors.count { var factor = factors[i] let size = max(MemoryLayout<Float>.size, 16) let buffer = device.makeBuffer( bytes: &factor, length: size, options: [.storageModeShared] ) commandEncoder.setBuffer(buffer, offset: 0, index: i) } commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup) commandEncoder.endEncoding() commandBuffer.present(drawable) commandBuffer.commit() } !42
  13. private let videoOutput = AVPlayerItemVideoOutput.init(pixelBufferAttributes: [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,

    ] as [String: Any]) func setupPlayer(url: URL) { player = AVPlayer(url: url) guard let player = player, let videoItem = player.currentItem else { return } videoItem.add(videoOutput) } private func makeCurrentVideoImage() -> CIImage? { guard let player = player, let videoItem = player.currentItem else { return nil } let time = videoItem.currentTime() guard videoOutput.hasNewPixelBuffer(forItemTime: time), let pixelBuffer = videoOutput.copyPixelBuffer(forItemTime: time, itemTimeForDisplay: nil) else { return nil } return CIImage(cvPixelBuffer: pixelBuffer) } !43
  14. private let videoOutput = AVPlayerItemVideoOutput.init(pixelBufferAttributes: [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,

    ] as [String: Any]) func setupPlayer(url: URL) { player = AVPlayer(url: url) guard let player = player, let videoItem = player.currentItem else { return } videoItem.add(videoOutput) } private func makeCurrentVideoImage() -> CIImage? { guard let player = player, let videoItem = player.currentItem else { return nil } let time = videoItem.currentTime() guard videoOutput.hasNewPixelBuffer(forItemTime: time), let pixelBuffer = videoOutput.copyPixelBuffer(forItemTime: time, itemTimeForDisplay: nil) else { return nil } return CIImage(cvPixelBuffer: pixelBuffer) } !44
  15. override func draw(_ dirtyRect: CGRect) { guard let device =

    device, let drawable = currentDrawable, let tempDrawable = bufferMtkView.currentDrawable, let image = makeCurrentVideoImage() else { return } ciContext.render(image, to: tempDrawable.texture, commandBuffer: nil, bounds: bounds, colorSpace: colorSpace) colorPixelFormat = tempDrawable.texture.pixelFormat let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeComputeCommandEncoder()! commandEncoder.setComputePipelineState(pipelineState) commandEncoder.setTexture(tempDrawable.texture, index: 0) commandEncoder.setTexture(drawable.texture, index: 1) let factors: [Float] = [ 0, // red 1, // green 0, // blue 0.43, // threshold 0.11 // smoothing ] for i in 0..<factors.count { var factor = factors[i] let size = max(MemoryLayout<Float>.size, 16) let buffer = device.makeBuffer( bytes: &factor, length: size, options: [.storageModeShared] ) commandEncoder.setBuffer(buffer, offset: 0, index: i) } commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup) commandEncoder.endEncoding() commandBuffer.present(drawable) commandBuffer.commit() } !46
  16. override func draw(_ dirtyRect: CGRect) { guard let device =

    device, let drawable = currentDrawable, let tempDrawable = bufferMtkView.currentDrawable, let image = makeCurrentVideoImage() else { return } ciContext.render(image, to: tempDrawable.texture, commandBuffer: nil, bounds: bounds, colorSpace: colorSpace) colorPixelFormat = tempDrawable.texture.pixelFormat let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeComputeCommandEncoder()! commandEncoder.setComputePipelineState(pipelineState) commandEncoder.setTexture(tempDrawable.texture, index: 0) commandEncoder.setTexture(drawable.texture, index: 1) let factors: [Float] = [ 0, // red 1, // green 0, // blue 0.43, // threshold 0.11 // smoothing ] for i in 0..<factors.count { var factor = factors[i] let size = max(MemoryLayout<Float>.size, 16) let buffer = device.makeBuffer( bytes: &factor, length: size, options: [.storageModeShared] ) commandEncoder.setBuffer(buffer, offset: 0, index: i) } commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup) commandEncoder.endEncoding() commandBuffer.present(drawable) commandBuffer.commit() } !48
  17. #include <metal_stdlib> using namespace metal; /// - SeeAlso: http://www.fundza.com/rman_shaders/smoothstep/index.html kernel

    void ChromaKeyFilter( // ೖྗը૾৘ใ texture2d<float, access::read> inTexture [[ texture(0) ]], // ग़ྗը૾৘ใ texture2d<float, access::write> outTexture [[ texture(1) ]], // ϚεΫର৅ͷ੺੒෼ const device float *colorRed [[ buffer(0) ]], // ϚεΫର৅ͷ྘੒෼ const device float *colorGreen [[ buffer(1) ]], // ϚεΫର৅ͷ੨੒෼ const device float *colorBlue [[ buffer(2) ]], // ಁա஋͕0-1ͷྖҬͷԼݶ const device float *threshold [[ buffer(3) ]], // ಁա஋͕0-1ͷྖҬͷԼݶ͔Β্ݶ·Ͱͷઈର஋ const device float *smoothing [[ buffer(4) ]], // ࠲ඪ৘ใ uint2 gid [[ thread_position_in_grid ]]) { const float4 inColor = inTexture.read(gid); // maskͷର৅ͷ৭ɻ྘৭Λಁա͢ΔͨΊmaskColor͸float3(0, 1, 0)ʹͳΔ const float3 maskColor = float3(*colorRed, *colorGreen, *colorBlue); // RGB͔ΒY΁ͷࣹ const float3 YVector = float3(0.2989, 0.5866, 0.1145); // maskColor(྘৭)ΛYCrCb΁ม׵ const float maskY = dot(maskColor, YVector); const float maskCr = 0.7131 * (maskColor.r - maskY); const float maskCb = 0.5647 * (maskColor.b - maskY); // ࠲ඪͷRGBΛYCrCb΁ม׵ const float Y = dot(inColor.rgb, YVector); const float Cr = 0.7131 * (inColor.r - Y); const float Cb = 0.5647 * (inColor.b - Y); // YCrCbɺᮢ஋ɺ྘৭ͱͷڑ཭ʹΑΓಁա౓Λࢉग़ const float alpha = smoothstep( *threshold, *threshold + *smoothing, distance(float2(Cr, Cb), float2(maskCr, maskCb)) ); // ࠲ඪͷRGBΛࢉग़ͨ͠ಁա౓Ͱग़ྗ͢Δ৭Λܭࢉ const float4 outColor = alpha * float4(inColor.r, inColor.g, inColor.b, 1.0); outTexture.write(outColor, gid); } !54
  18. #include <metal_stdlib> using namespace metal; /// - SeeAlso: http://www.fundza.com/rman_shaders/smoothstep/index.html kernel

    void ChromaKeyFilter( // ೖྗը૾৘ใ texture2d<float, access::read> inTexture [[ texture(0) ]], // ग़ྗը૾৘ใ texture2d<float, access::write> outTexture [[ texture(1) ]], // ϚεΫର৅ͷ੺੒෼ const device float *colorRed [[ buffer(0) ]], // ϚεΫର৅ͷ྘੒෼ const device float *colorGreen [[ buffer(1) ]], // ϚεΫର৅ͷ੨੒෼ const device float *colorBlue [[ buffer(2) ]], // ಁա஋͕0-1ͷྖҬͷԼݶ const device float *threshold [[ buffer(3) ]], // ಁա஋͕0-1ͷྖҬͷԼݶ͔Β্ݶ·Ͱͷઈର஋ const device float *smoothing [[ buffer(4) ]], // ࠲ඪ৘ใ uint2 gid [[ thread_position_in_grid ]]) { !55
  19. { const float4 inColor = inTexture.read(gid); // maskͷର৅ͷ৭ɻ྘৭Λಁա͢ΔͨΊmaskColor͸float3(0, 1, 0)ʹͳΔ

    const float3 maskColor = float3(*colorRed, *colorGreen, *colorBlue); // RGB͔ΒY΁ͷࣹ const float3 YVector = float3(0.2989, 0.5866, 0.1145); // maskColor(྘৭)ΛYCrCb΁ม׵ const float maskY = dot(maskColor, YVector); const float maskCr = 0.7131 * (maskColor.r - maskY); const float maskCb = 0.5647 * (maskColor.b - maskY); // ࠲ඪͷRGBΛYCrCb΁ม׵ const float Y = dot(inColor.rgb, YVector); const float Cr = 0.7131 * (inColor.r - Y); const float Cb = 0.5647 * (inColor.b - Y); // YCrCbɺᮢ஋ɺ྘৭ͱͷڑ཭ʹΑΓಁա౓Λࢉग़ const float alpha = smoothstep( *threshold, *threshold + *smoothing, distance(float2(Cr, Cb), float2(maskCr, maskCb)) ); // ࠲ඪͷRGBΛࢉग़ͨ͠ಁա౓Ͱग़ྗ͢Δ৭Λܭࢉ const float4 outColor = alpha * float4(inColor.r, inColor.g, inColor.b, 1.0); outTexture.write(outColor, gid); } !56
  20. !57

  21. !59

  22. !60

  23. override func draw(_ dirtyRect: CGRect) { guard let device =

    device, let drawable = currentDrawable, let tempDrawable = bufferMtkView.currentDrawable, let image = makeCurrentVideoImage() else { return } ciContext.render(image, to: tempDrawable.texture, commandBuffer: nil, bounds: bounds, colorSpace: colorSpace) colorPixelFormat = tempDrawable.texture.pixelFormat let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeComputeCommandEncoder()! commandEncoder.setComputePipelineState(pipelineState) commandEncoder.setTexture(tempDrawable.texture, index: 0) commandEncoder.setTexture(drawable.texture, index: 1) let factors: [Float] = [ 0, // red 1, // green 0, // blue 0.43, // threshold 0.11 // smoothing ] for i in 0..<factors.count { var factor = factors[i] let size = max(MemoryLayout<Float>.size, 16) let buffer = device.makeBuffer( bytes: &factor, length: size, options: [.storageModeShared] ) commandEncoder.setBuffer(buffer, offset: 0, index: i) } commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup) commandEncoder.endEncoding() commandBuffer.present(drawable) commandBuffer.commit() } !67
  24. override func draw(_ dirtyRect: CGRect) { guard let device =

    device, let drawable = currentDrawable, let tempDrawable = bufferMtkView.currentDrawable, let image = makeCurrentViewImage() else { return } ciContext.render(image, to: tempDrawable.texture, commandBuffer: nil, bounds: bounds, colorSpace: colorSpace) colorPixelFormat = tempDrawable.texture.pixelFormat let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeComputeCommandEncoder()! commandEncoder.setComputePipelineState(pipelineState) commandEncoder.setTexture(tempDrawable.texture, index: 0) commandEncoder.setTexture(drawable.texture, index: 1) let factors: [Float] = [ 0, // red 1, // green 0, // blue 0.43, // threshold 0.11 // smoothing ] for i in 0..<factors.count { var factor = factors[i] let size = max(MemoryLayout<Float>.size, 16) let buffer = device.makeBuffer( bytes: &factor, length: size, options: [.storageModeShared] ) commandEncoder.setBuffer(buffer, offset: 0, index: i) } commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup) commandEncoder.endEncoding() commandBuffer.present(drawable) commandBuffer.commit() } !68
  25. func makeCurrentViewImage(view: UIView) -> CIImage? { UIGraphicsBeginImageContextWithOptions(view.frame.size, true, 0) view.drawHierarchy(in:

    view.bounds, afterScreenUpdates: false) let image = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext(); return CIImage(image: image) } !69
  26. !71

  27. !72

  28. !74

  29. ؔ࿈৘ใɾΫϨδοτ άϦʔϯόοΫΞΠυϧɹ.JLB 3JLB IUUQTXXXZPVUVCFDPNXBUDI WX0OTBMKG"P )PMPHSBQIJD1SPKFDUJPO IUUQTXXXTIBEFSUPZDPNWJFX.UK9[N <J04><"3,JU>ۙॴͷறं৔ͰɺେܕσΟεϓϨΠͰϏσΦΛݟΔʂ IUUQTEFWDMBTTNFUIPEKQTNBSUQIPOFJQIPOFJPTBSLJUWJEFP "3,JUͷͨΊͷ%਺ֶ

    IUUQTRJJUBDPNLCPZJUFNTGFGEEBDGC "3,JU4QSJUF,JUTFUQJYFM#V⒎FS"UUSJCVUFTUP4,7JEFP/PEFPSNBLFUSBOTQBSFOUQJYFMTJOWJEFP DISPNBLFZF⒎FDU BOPUIFSXBZ IUUQTTUBDLPWFSqPXDPNRVFTUJPOTBSLJUTQSJUFLJUTFUQJYFMCV⒎FSBUUSJCVUFTUPTLWJEFPOPEFPSNBLFUSBOTQBSFOU )&7$7JEFPXJUI"MQIB IUUQTEFWFMPQFSBQQMFDPNWJEFPTQMBZXXED !80
  30. !86  ΫϩϚΩʔॲཧ$*'JMUFSΛDBQUVSF0VUQVU @EJE0VUQVUGSPN Ͱద༻  4,&⒎FDU/PEFʹΫϩϚΩʔॲཧ$*'JMUFSઃఆ  4$/.BUFSJBMͷTIBEFS.PEJpFSTʹΫϩϚΩʔγΣʔμʔॲཧઃఆ 

    4$/.BUFSJBMʹΞϧϑΝ஋ઃఆγΣʔμʔॲཧɺUSBOTQBSFOUDPOUFOUTʹϚεΫಈըઃఆ  όοϑΝྖҬʹΫϩϚΩʔγΣʔμʔద༻݁ՌΛอ࣋͠ඳըʹ࢖༻