画像エディタのアノテーションで指定した範囲を下絵にするアドオン

以前記事にした [Blenderのカメラ操作に関するスクリプト](https://qiita.com/yukimituki11/items/cd0909864d3602dc53d1)の
画像エディタに表示されてる画像の指定範囲をカメラの下絵にするスクリプトを
少し整理して　アドオンにしてみました　画像タブにAnnotation to boundパネルを追加しています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/cacd42be-52ca-914e-211e-cdec09cf09b8.png)
アノテーションの線が書かれている範囲がカメラの下絵になっているのが分かるでしょうか？
指定した範囲の解像度でレンダリング解像度も指定するようになっています。

これ自体は非常にニッチな機能だけれど
応用すれば面白い使い方ができるかもしれません。

```py:CropBG_Annotate.py
bl_info = {
    "name" : "Crop image from Annotation",
    "author" : "Yukimituki",
    "description" : "",
    "blender" : (2, 80, 0),
    "version" : (0, 0, 1),
    "location" : "",
    "warning" : "",
    "category" : "Generic"
}

import bpy
import os
from bpy.types import (
        PropertyGroup,
        )

class IMAGE_PT_Annotatebound(bpy.types.Panel):
    bl_idname = "annotation_to_bound.panel"
    bl_label = "Annotation to bound"
    bl_space_type = "IMAGE_EDITOR"
    bl_region_type = "UI"
    bl_category = "Image"
    # 描画の定義
    def draw(self, context):
        layout = self.layout
        row = layout.row()
        row.operator("action.crop_stroke_bound")

class IMAGE_OT_Stroke(bpy.types.Operator):
    ''' アノテーションで指定した範囲を切り抜き '''
    bl_idname = "action.crop_stroke_bound"
    bl_label = "get_stroke"
    def execute(self, context):
        scene = context.scene
        # アノテーションの線の取得
        strokes = context.annotation_data.layers.active.active_frame.strokes
        # アノテーションは3Dビューに使われるものと3Dビューに使われるのがあるのでフィルタリング
        strokes_2d = [ s for s in strokes if s.display_mode == '2DSPACE']
        bbox_2d = self.get_stroke_bound( strokes_2d )

        # 表示している画像の情報を取得
        img = context.space_data.image
        #カメラの下絵画像を設定
        crop_size = self.set_cameraBG(context, img, bbox_2d)
        # レンダリングの画像サイズの設定
        scene.render.resolution_x = crop_size[0]
        scene.render.resolution_y = crop_size[1]
        return {'FINISHED'}
        
    def get_stroke_bound(self, strokes):
        '''アノテーションのある範囲の取得'''
        x_list = []
        y_list = []
        for stroke in strokes:
            x_list += [pos.co[0] for pos in stroke.points]
            y_list += [pos.co[1] for pos in stroke.points]
        bbox = [min(x_list),min(y_list), max(x_list), max(y_list)]
        return(bbox)
    def set_cameraBG(self, context, image_object, bbox):
        '''カメラの背景を設定'''
        #カメラの情報を取得
        camera = context.scene.camera
        camera.data.show_background_images = True

        # 下絵画像の設定
        bg_image = camera.data.background_images.new()
        bg_image.image = image_object
        bg_size = image_object.size

        # 下絵の表示される範囲の設定
        bg_image.frame_method = 'CROP'
        crop_size = [(bbox[2] -bbox[0])*bg_size[0], (bbox[3] -bbox[1])*bg_size[1]]
        # 背景のトリムは背景画像の比率と比べて大きい方を基準にフレームに収めて切り抜く
        if bg_size[0] * crop_size[0] > bg_size[1] * crop_size[1]:
            bg_scale = bg_size[0] / crop_size[0]
        else:
            bg_scale = bg_size[1] / crop_size[1]
        bg_image.scale = bg_scale
        # 下絵の中心位置を設定
        bg_image.offset = ((bbox[0] + bbox[2] -1) *-0.5 *bg_scale, (bbox[1]+ bbox[3] -1) *-0.5 *bg_scale) 
        return( crop_size )

classes = (
    IMAGE_PT_Annotatebound,
    IMAGE_OT_Stroke,
    )

#####################################
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    #bpy.app.translations.register(__name__, translation_dict)   # 辞書の登録


def unregister():
    #bpy.app.translations.unregister(__name__)   # 辞書の削除
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()
#######################################
```