Skip to content
Snippets Groups Projects
render_auto_save.py 8.37 KiB
Newer Older
  • Learn to ignore specific revisions
  • florianfelix's avatar
    florianfelix committed
    # ##### BEGIN GPL LICENSE BLOCK #####
    
    Florian Meyer's avatar
    Florian Meyer committed
    #
    
    florianfelix's avatar
    florianfelix committed
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    
    Florian Meyer's avatar
    Florian Meyer committed
    #
    
    florianfelix's avatar
    florianfelix committed
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    
    Florian Meyer's avatar
    Florian Meyer committed
    #
    
    florianfelix's avatar
    florianfelix committed
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    
    Florian Meyer's avatar
    Florian Meyer committed
    #
    
    florianfelix's avatar
    florianfelix committed
    # ##### END GPL LICENSE BLOCK #####
    
    
    bl_info = {
        "name": "Auto Save Render",
    
    florianfelix's avatar
    florianfelix committed
        "author": "tstscr(florianfelix)",
        "version": (2, 1),
        "blender": (2, 80, 0),
        "location": "Rendertab -> Output Panel -> Subpanel",
    
        "description": "Automatically save the image after rendering",
        "warning": "",
    
        "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Render/Auto_Save",
    
        "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
    
        "category": "Render"}
    
    florianfelix's avatar
    florianfelix committed
    
    
    
    import bpy
    
    florianfelix's avatar
    florianfelix committed
    from bpy.types import Panel
    
    from bpy.props import BoolProperty, EnumProperty
    from bpy.app.handlers import persistent
    from os.path import dirname, exists, join
    from bpy.path import basename
    from os import mkdir, listdir
    
    from re import findall, search
    
    IMAGE_FORMATS = (
        'BMP',
        'IRIS',
        'PNG',
        'JPEG',
        'JPEG2000',
        'TARGA',
        'TARGA_RAW',
        'CINEON',
        'DPX',
        'OPEN_EXR_MULTILAYER',
        'OPEN_EXR',
        'HDR',
        'TIFF')
    IMAGE_EXTENSIONS = (
        'bmp',
        'rgb',
        'png',
        'jpg',
        'jp2',
        'tga',
        'cin',
        'dpx',
        'exr',
        'hdr',
        'tif'
    )
    
    
    
    @persistent
    def auto_save_render(scene):
    
    florianfelix's avatar
    florianfelix committed
        if not scene.auto_save_after_render or not bpy.data.filepath:
    
            return
        rndr = scene.render
        original_format = rndr.image_settings.file_format
    
    
        if scene.auto_save_format == 'SCENE':
            if original_format not in IMAGE_FORMATS:
                print('{} Format is not an image format. Not Saving'.format(
                    original_format))
                return
        elif scene.auto_save_format == 'PNG':
            rndr.image_settings.file_format = 'PNG'
        elif scene.auto_save_format == 'OPEN_EXR_MULTILAYER':
            rndr.image_settings.file_format = 'OPEN_EXR_MULTILAYER'
        elif scene.auto_save_format == 'JPEG':
            rndr.image_settings.file_format = 'JPEG'
    
    
        frame_current = bpy.context.scene.frame_current
    
        extension = rndr.file_extension
    
        blendname = basename(bpy.data.filepath).rpartition('.')[0]
        filepath = dirname(bpy.data.filepath) + '/auto_saves'
    
        if not exists(filepath):
            mkdir(filepath)
    
        if scene.auto_save_subfolders:
            filepath = join(filepath, blendname)
            if not exists(filepath):
                mkdir(filepath)
    
    
        # imagefiles starting with the blendname
    
    florianfelix's avatar
    florianfelix committed
        files = [f for f in listdir(filepath)
                 if f.startswith(blendname)
    
                 and f.lower().endswith(IMAGE_EXTENSIONS)]
    
        def save_number_from_files(files):
    
            '''
            Returns the new highest count number from file names
            as 3 digit string.
            '''
    
            highest = 0
            if files:
                for f in files:
    
                    # find last numbers in the filename
    
                    suffix = findall(r'\d+', f.split(blendname)[-1])
                    if suffix:
                        if int(suffix[-1]) > highest:
                            highest = int(suffix[-1])
            return str(highest+1).zfill(3)
    
        def this_frame_files(files):
    
            '''
            Filters out files which have the current frame number in the file name
            '''
    
            match_files = []
            frame_pattern = r'_f[0-9]{4}_'
            for file in files:
                res = search(frame_pattern, file)
                if res:
                    if int(res[0][2:-1]) == frame_current:
                        match_files.append(file)
            return match_files
    
        if scene.auto_save_use_framenumber:
    
            if scene.auto_save_use_continuous:
                save_number = save_number_from_files(files)
            else:
                frame_files = this_frame_files(files)
                save_number = save_number_from_files(frame_files)
    
            frame_number = 'f' + str(frame_current).zfill(4)
            save_name = '_'.join([blendname, frame_number, save_number])
        else:
            save_number = save_number_from_files(files)
            save_name = '_'.join([blendname, save_number])
        save_name += extension
        save_name = join(filepath, save_name)
    
    
        image = bpy.data.images['Render Result']
        if not image:
            print('Auto Save: Render Result not found. Image not saved')
            return
    
        print('Auto_Save:', save_name)
        image.save_render(save_name, scene=None)
    
    
    florianfelix's avatar
    florianfelix committed
        if scene.auto_save_blend:
    
            save_name_blend = join(filepath, save_name) + '.blend'
    
    florianfelix's avatar
    florianfelix committed
            print('Blend_Save:', save_name_blend)
            bpy.ops.wm.save_as_mainfile(filepath=save_name_blend, copy=True)
    
        rndr.image_settings.file_format = original_format
    
    ###########################################################################
    
    florianfelix's avatar
    florianfelix committed
    class RENDER_PT_render_auto_save(Panel):
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "render"
        bl_label = "Auto Save Render"
        bl_parent_id = "RENDER_PT_output"
        bl_options = {'DEFAULT_CLOSED'}
    
        COMPAT_ENGINES = {'CYCLES', 'BLENDER_RENDER',
                          'BLENDER_EEVEE', 'BLENDER_OPENGL'}
    
    florianfelix's avatar
    florianfelix committed
    
        @classmethod
        def poll(cls, context):
            return (context.engine in cls.COMPAT_ENGINES)
    
        def draw_header(self, context):
            self.layout.prop(context.scene, 'auto_save_after_render', text="")
    
        def draw(self, context):
            layout = self.layout
            layout.use_property_split = True
            layout.use_property_decorate = False  # No animation.
    
            col = layout.column(align=True)
    
            col.prop(context.scene, 'auto_save_format', text='as', expand=False)
    
    florianfelix's avatar
    florianfelix committed
            col.prop(context.scene, 'auto_save_blend', toggle=False)
            col.prop(context.scene, 'auto_save_subfolders', toggle=False)
    
            col.prop(context.scene, 'auto_save_use_framenumber', toggle=False)
            # subcol = col.column()
            # subcol.active = context.scene.auto_save_use_framenumber
            # subcol.prop(context.scene, 'auto_save_use_continuous', toggle=False)
    
    florianfelix's avatar
    florianfelix committed
    
    
    classes = [
        RENDER_PT_render_auto_save,
    ]
    
    
    def register():
    
    florianfelix's avatar
    florianfelix committed
        from bpy.utils import register_class
        for cls in classes:
            register_class(cls)
    
        bpy.types.Scene.auto_save_after_render = BoolProperty(
            name='Save after render',
            default=False,
            description='Automatically save rendered images into: //auto_save/')
        bpy.types.Scene.auto_save_blend = BoolProperty(
            name='Save .blend (copy) alongside image',
            default=False,
            description='Also save .blend (copy) file into: //auto_save/')
    
        bpy.types.Scene.auto_save_format = EnumProperty(
    
    florianfelix's avatar
    florianfelix committed
            name='Auto Save File Format',
            description='File Format for the auto saves.',
    
            items=[
                ('SCENE', 'scene format', 'Format set in output panel'),
    
    florianfelix's avatar
    florianfelix committed
                ('PNG', 'png', 'Save as png'),
                ('JPEG', 'jpg', 'Save as jpg'),
    
                ('OPEN_EXR_MULTILAYER', 'exr', 'Save as multilayer exr'),
                ],
            default='SCENE')
    
        bpy.types.Scene.auto_save_subfolders = BoolProperty(
    
    florianfelix's avatar
    florianfelix committed
            name='Save into subfolder',
            default=False,
            description='Save into individual subfolders per blend name')
    
        bpy.types.Scene.auto_save_use_framenumber = BoolProperty(
            name='Insert frame number',
            default=False,
            description='Insert frame number into file name'
        )
    
        bpy.types.Scene.auto_save_use_continuous = BoolProperty(
            name='Continuous numbering',
            default=False,
            description='Use continuous numbering when inserting frame numbers'
        )
    
        bpy.app.handlers.render_post.append(auto_save_render)
    
    florianfelix's avatar
    florianfelix committed
    
    
    def unregister():
    
    florianfelix's avatar
    florianfelix committed
        from bpy.utils import unregister_class
        for cls in reversed(classes):
            unregister_class(cls)
    
        del(bpy.types.Scene.auto_save_after_render)
    
        del(bpy.types.Scene.auto_save_format)
        del(bpy.types.Scene.auto_save_subfolders)
    
        del(bpy.types.Scene.auto_save_use_framenumber)
    
        del(bpy.types.Scene.auto_save_use_continuous)
    
        bpy.app.handlers.render_post.remove(auto_save_render)
    
    florianfelix's avatar
    florianfelix committed
    
    
    
    if __name__ == "__main__":
    
        register()