top of page

Procreate exported video black frames fix (and size too)!

My 11" iPad is shiny and new, so I'm surprised black frames still get saved in the output video from Procreate. They would be tedious to edit out of a video on a timeline, so I wondered if chatgpt could write me a pythonscript to resave the MP4 without black frames by sending the non-black frames to PNG (in a temp folder) then repacking them to a new MP4. There's no way, I thought... I'll be here for hours, I thought... but I was wrong. It worked immediately. Writing this post took considerably longer. Requirements

  • Python 3

  • OpenCV for Python (pip install opencv-python)

  • ffmpeg installed and available on the system path It's funny that chatgpt suggested to use FFMPEG because I had just done watching the riff on FFMPEG from Programmers are Also Human. I had to overcome a moment of nerves before going ahead and installing it.


To have FFmpeg installed in Windows and available on the system PATH, you need to:

  1. Install FFmpeg

    • Windows:

      • Download the latest FFmpeg “release” zip from the official FFmpeg page or a trusted source.

      • Extract the contents to a folder (for example, D:\Dropbox\ffmpeg).

  2. Add the FFmpeg binary folder to your PATH

    • Windows:

      • Find the directory that holds the ffmpeg.exe file (e.g., D:\Dropbox\ffmpeg\bin).

      • Go to Start → Var... Edit System Environment Variables.

      • Under System variables, scroll down to Path, select it, and click Edit.

      • Click New, then paste the path to the bin folder (e.g., D:\Dropbox\ffmpeg\bin).

      • Click OK to close all dialogs.

      • Go to Start → CMD (command prompt) and run ffmpeg -version to verify.


    The python can be added using Start → IDLE > File > New Script and saved to RemoveBlackFrames.py

Python RemoveBlackFrames.py

#!/usr/bin/env python3


import cv2

import os

import shutil

import subprocess

import uuid


def remove_black_frames(input_video_path, only_last_59s=False, remove_temp_folder=True):

# Validate file

if not os.path.isfile(input_video_path):

print(f"Error: File '{input_video_path}' does not exist.")

return


# Extract file details

input_dir = os.path.dirname(os.path.abspath(input_video_path))

input_name = os.path.splitext(os.path.basename(input_video_path))[0]

output_video_path = os.path.join(input_dir, f"{input_name}_result.mp4")


# Create a unique temp directory for frame extraction

temp_dir = os.path.join(input_dir, f"temp_frames_{uuid.uuid4().hex}")

os.makedirs(temp_dir, exist_ok=True)


# Open video capture

cap = cv2.VideoCapture(input_video_path)

if not cap.isOpened():

print(f"Error: Could not open '{input_video_path}' for reading.")

return


# Get properties

fps = cap.get(cv2.CAP_PROP_FPS)

total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))


# If only_last_59s is True, skip to the frame that starts 59s from the end

if only_last_59s:

frames_59s = int(fps * 59)

start_frame = max(0, total_frames - frames_59s)

cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)


frame_count = 0

saved_count = 0


while True:

ret, frame = cap.read()

if not ret:

break


frame_count += 1

# Check if current frame is fully black

if frame.max() != 0:

saved_count += 1

frame_filename = os.path.join(temp_dir, f"frame_{saved_count:06d}.png")

cv2.imwrite(frame_filename, frame)


cap.release()


if saved_count == 0:

print("All frames were black or no frames found. No output video created.")

if remove_temp_folder:

shutil.rmtree(temp_dir)

return

else:

# Use ffmpeg to combine the frames into a new mp4

cmd = [

"ffmpeg",

"-y",

"-framerate", str(fps),

"-i", os.path.join(temp_dir, "frame_%06d.png"),

"-c:v", "libx264",

"-pix_fmt", "yuv420p",

output_video_path

]

subprocess.run(cmd, check=True)


if remove_temp_folder:

# Clean up temp frames

shutil.rmtree(temp_dir)


print(f"Done. Output saved to: {output_video_path}")

if not remove_temp_folder:

print(f"The temporary folder is located at: {temp_dir}")


if __name__ == "__main__":

# Replace the string below with your own path to the input MP4 file

input_video_path = r"C:\Users\ipad4\Desktop\Skippy\Skippy.mp4"


# Ask the user if they only want the final 59s

user_answer_59s = input("Do you want to encode only the final 59 seconds? (Y/N): ").strip().upper()

only_last_59s = (user_answer_59s == "Y")


# Ask the user if they want to remove the temp folder

user_answer_temp = input("Do you want to remove the temp folder after encoding? (Y/N): ").strip().upper()

remove_temp_folder = (user_answer_temp == "Y")


remove_black_frames(

input_video_path,

only_last_59s=only_last_59s,

remove_temp_folder=remove_temp_folder

)

Note that the input video path is hard-coded in the python file. Simply change that to your own video's file path. input_video_path = "C:\\Users\\ipad4\\Desktop\\Skippy\\Skippy.mp4"


During processing (which may take a while if the video is long), look in the folder that has the input file. You should see the video frames in a subfolder (as PNG) and the MP4 result of removing the black frames. Bonus : the output video will be far smaller file size than Procreate's exported video.



Temp folder with frames that will be encoded to result MP4
Output MP4 file size is way smaller!

Note that the subfolder of extracted frames is removed by default on completion of the task. If you want to keep the subfolder of frames to get at them individually afterwards, removed this line from the python:

shutil.rmtree(temp_dir)

[Update: I asked ChatGPT to modify the script to provide an option to keep the PNG folder. So now users have to choose Y / N for that. Also, because Bluesky only allows 60s video limit, I asked that the python have an option to export the full length or just the final 59s. Users have to enter Y / N for that option too when running the script from IDLE. Again, this worked first time (took me no real effort, which I like).]

The encoding of the PNGs is pretty fast once they are extracted. You may see FFMPEG pop-up its logging:

Machine powered task automation!

The orginal video is here (32mb) : https://www.dropbox.com/s/tkjsmpd70ygx3qz/Skippy.mp4?dl=0 The resultiing video, shown below, is 2mb.



Commentaires


Featured Posts
Recent Posts
Search By Tags
Follow Us
  • Facebook Basic Square
  • Twitter Basic Square
  • Google+ Basic Square
bottom of page