diff --git a/.gitignore b/.gitignore index ccc9fd9..d1f1814 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,11 @@ -*.DS_Store \ No newline at end of file +*.DS_Store +filmless_processing/data/*.TIF +filmless_processing/data/*.PNG +filmless_processing/data/frames/*.png +filmless_processing/data/frames/*.jpeg +filmless_processing/data/frames/*.jpg +filmless_processing/data/frames/*.tif +filmless_processing/data/frames/*.PNG +filmless_processing/data/frames/*.JPEG +filmless_processing/data/frames/*.JPG +filmless_processing/data/frames/*.TIF \ No newline at end of file diff --git a/README.md b/README.md index 59ca18d..0e1a4f9 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ sh scripts/export.sh /path/to/my/video.mov Tip for macOS Terminal users: You can get an absolute path to any file by simply dragging it into Terminal. Type "sh scripts/export " including the space at the end and then drag your video into Terminal and hit enter. VoilĂ . -This will export the video in a *high quality* .png sequence to a folder on your Desktop in a folder named "frames". This is the default directory that the `filmless_processing.pde` sketch will look for an image sequence. If your video contained any audio, it will be exported to a mono file named "audio.wav" in the "audio" folder now on your desktop. Otherwise you may see an error message in your terminal telling you that it couldn't find a stream. Not to worry. +This will export the video in a *high quality* .png sequence to the data folder in your filmless_processing sketch in a folder named "frames". This is the default directory that the `filmless_processing.pde` sketch will look for an image sequence. If your video contained any audio, it will be exported to a mono file named "audio.wav" in the "audio" folder now in the "audio" folder, next to "frames". Otherwise you may see an error message in your terminal telling you that it couldn't find a stream. Not to worry. You don't need to use this script to export your video to image sequences. You can use the application of choice to create your image sequences for the Processing sketch. This script simply lets you do that from the command line without opening up an NLE or media export program. @@ -133,7 +133,7 @@ sh scripts/calibration.sh ### pages.sh -Similar to the calibration script, this will convert your output .tif files from `filmless_processing.pde` to the correct DPI. Also set the `DPI` variable in this script to the one used in your Processing sketch. By default, this looks for files named `page_*.tif` on your Desktop. +Similar to the calibration script, this will convert your output .tif files from `filmless_processing.pde` to the correct DPI. Also set the `DPI` variable in this script to the one used in your Processing sketch. By default, this looks for files named `page_*.tif` in the filmless_processing/data folder. Unlike the calibration script, this does not convert your image to .png but maintains the TIFF format. @@ -241,28 +241,10 @@ The scripts are tested on macOS and Linux, but can be converted to work with Win There are variables at the beginning of the sketch that you will need to change to properly generate pages from your images. ```java -String DESKTOP = System.getProperty("user.home") + "/Desktop"; +int DPI = 1440; ``` -This line does not need to be used or changed, but will find the "Desktop" folder for the current user. This is useful if you wish to place your files in an easy-to-reach destination while generating files. - -```java -String SOURCE = DESKTOP + "/frames/"; -``` - -The `SOURCE` variable will point to the directory containing your image sequence. This can be changed completely or used as is if you place your image sequence in a folder named "frames" on your desktop and include the images. - -```java -String SOUND = DESKTOP + "/audio/audio.wav"; -``` - -The `SOUND` variable is optional. To include sound, use the path of a mono audio file. In the default setting, it will look for a file named "audio.wav" in a folder named "audio" on your Desktop. If you do not wish to use sound, change the line to this to generate a silent track: `String SOUND = "";` - -```java -String RENDER_PATH = DESKTOP + "/"; -``` - -This variable controls the output location for the `page_#.tif` files that will be produced by the sketch. These files can be LARGE (500MB to 2GB) so point this to a directory with a lot of space. Keep in mind, you will produce one of these pages for every 396 frames using the default settings, so a 4000 frame sequence will produce 11 pages. That as much as 22GB (or more) so plan accordingly. +The DPI is the target for printing. The maximum DPI you'll be able to print is dependent on your printer or image reproduction technology. The higher the DPI, the higher the theoretical resolution of your output. This variable will also determine your sound quality, as you will only be able to reproduce the number of samples the vertical resolution allows. If your printer can only draw 10,000 lines in 24 frames, your sample rate will effectively be 10Khz. ```java String SOUNDTRACK_TYPE = "unilateral"; @@ -270,12 +252,6 @@ String SOUNDTRACK_TYPE = "unilateral"; The soundtrack type refers to the style of soundtrack that's produced by the sketch. The soundtrack is produced using [SoundtrackOptical](https://github.com/sixteenmillimeter/SoundtrackOptical) and the options are `unilateral`, `variable area`, `dual variable area`, `maurer`, `variable density`. Read more about these different types [here](http://www.paulivester.com/films/filmstock/guide.htm). -```java -int DPI = 1440; -``` - -The DPI is the target for printing. The maximum DPI you'll be able to print is dependent on your printer or image reproduction technology. The higher the DPI, the higher the theoretical resolution of your output. This variable will also determine your sound quality, as you will only be able to reproduce the number of samples the vertical resolution allows. If your printer can only draw 10,000 lines in 24 frames, your sample rate will effectively be 10Khz. - ```java String PITCH = "long"; ``` diff --git a/filmless_processing/data/audio/placeholder.txt b/filmless_processing/data/audio/placeholder.txt new file mode 100644 index 0000000..e69de29 diff --git a/filmless_processing/data/frames/placeholder.txt b/filmless_processing/data/frames/placeholder.txt new file mode 100644 index 0000000..e69de29 diff --git a/filmless_processing/data/placeholder.txt b/filmless_processing/data/placeholder.txt new file mode 100644 index 0000000..e69de29 diff --git a/filmless_processing/filmless_processing.pde b/filmless_processing/filmless_processing.pde index 4bd9824..55be209 100644 --- a/filmless_processing/filmless_processing.pde +++ b/filmless_processing/filmless_processing.pde @@ -10,14 +10,9 @@ import soundtrack.optical.*; * CHANGE THESE **/ -String DESKTOP = System.getProperty("user.home") + "/Desktop"; -String SOURCE = DESKTOP + "/frames/"; //path to directory containing frames -String SOUND = DESKTOP + "/audio/audio.wav"; //leave empty string if silent -String RENDER_PATH = DESKTOP + "/"; //path to directory where pages will be placed - //types: unilateral, variable area, dual variable area, maurer, variable density -String SOUNDTRACK_TYPE = "unilateral"; -int DPI = 1440; //maximum printer DPI +int DPI = 1200; //maximum printer DPI +String SOUNDTRACK_TYPE = "unilateral"; String PITCH = "long"; // long, short //7.62, 7.605 String FORMAT = "16mm"; //16mm or super16 int PERFS = 1; //single (1) or double (2) perf film @@ -31,6 +26,12 @@ boolean SHOW_PERFS = true; //set to true to print perfs for cutting registrat color PERFS_COLOR = color(255); int SOUND_OFFSET = 25; +//Don't change unless necessary +String SEP = System.getProperty("file.separator"); + +String SOURCE = "frames"; //path to directory containing frames +String SOUND = "audio"; //leave empty string if silent + //This is a magic number that is used to scale the vertical (H) or horizontal (W) resolution //because the printer sometimes lies to you. float MAGIC_H_CORRECTION = 1.0; @@ -62,6 +63,8 @@ int FRAME_LINE = round((SPACING - FRAME_H) / 2); int PAGES = 0; int FRAMES = 0; int SOUND_W = ceil(DPMM * (12.52 - 10.26)); +boolean HAS_SOUND = false; +String SOUNDTRACK_FILE = ""; SoundtrackOptical soundtrack; String[] frames; @@ -74,27 +77,29 @@ void setup () { size(640, 480); //surface.setResizable(true); println(SOURCE); - frames = listFrames(SOURCE); + println(SOUND); + frames = listFrames(SOURCE, SOUND); if (frames == null) { println("Frames not found, check SOURCE path"); exit(); + return; } FRAMES = frames.length; PAGES = ceil((float) FRAMES / (ROWS * COLUMNS)); pageBuffer = createGraphics(PAGE_W_PIXELS, PAGE_H_PIXELS); - if (!SOUND.equals("")) { - soundtrack = new SoundtrackOptical(this, SOUND, DPI, 1.0, SOUNDTRACK_TYPE, PITCH, !NEGATIVE); + if (HAS_SOUND) { + soundtrack = new SoundtrackOptical(this, SOUNDTRACK_FILE, DPI, 1.0, SOUNDTRACK_TYPE, PITCH, !NEGATIVE); } printInfo(); - if (PERFS == 2 && !SOUND.equals("") ) { + if (PERFS == 2 && HAS_SOUND ) { println("WARNING: Double perf film and soundtrack will interfere with one another. Are you sure?"); } - if (FORMAT.equals("super16") && !SOUND.equals("")) { + if (FORMAT.equals("super16") && HAS_SOUND) { println("WARNING: Super16 frame and soundtrack will interfere with one another. Are you sure?"); } @@ -139,37 +144,73 @@ void printInfo() { } } -String[] listFrames (String dir) { +String[] listFrames (String dir, String audioDir) { ArrayList tmp = new ArrayList(); + ArrayList audioTmp = new ArrayList(); String output[]; File file; + File audioFile; int arraySize; int o = 0; - if (dir.substring(dir.length() - 1, dir.length()) != "/") { - dir = dir + "/"; + dir = dataPath(dir); + audioDir = dataPath(audioDir); + println(dir); + println(audioDir); + if (dir.substring(dir.length() - 1, dir.length()) != SEP) { + dir = dir + SEP; + } + if (audioDir.substring(audioDir.length() - 1, audioDir.length()) != SEP) { + audioDir = audioDir + SEP; } file = new File(dir); + audioFile = new File(SOUND); if (file.isDirectory()) { String names[] = file.list(); names = sort(names); for (int i = 0; i < names.length; i++) { - if (names[i].toLowerCase().contains(".jpg") || + if (names[i].toLowerCase().contains(".jpg") || names[i].toLowerCase().contains(".jpeg") || - names[i].toLowerCase().contains(".tif") || //only works with Processing tiffs + names[i].toLowerCase().contains(".tif") || //only works with Processing tiffs names[i].toLowerCase().contains(".png")) { tmp.add(dir + names[i]); } } arraySize = tmp.size(); + + if (arraySize == 0) { + println("ERROR: No frames detected, exiting"); + exit(); + return null; + } + + String audioNames[] = audioFile.list(); + if (audioNames != null) { + audioNames = sort(audioNames); + + for (int i = 0; i < audioNames.length; i++) { + if (audioNames[i].toLowerCase().contains(".wav")) { + audioTmp.add(audioDir + audioNames[i]); + } + } + } + + if (audioTmp.size() > 0) { + HAS_SOUND = true; + SOUNDTRACK_FILE = audioTmp.get(0); + println("Using audio file " + SOUNDTRACK_FILE); + } else { + println("No audio file detected, creating silent tracks"); + } + - if (!SOUND.equals("")) { + if (HAS_SOUND) { arraySize += SOUND_OFFSET; } output = new String[arraySize]; - if (!SOUND.equals("")) { + if (HAS_SOUND) { for (int i = 0; i < SOUND_OFFSET; i++) { output[o] = "_BLANK_"; o++; @@ -183,8 +224,10 @@ String[] listFrames (String dir) { sort(output); return output; } else { - return null; + println("ERROR: SOURCE variable does not point to a directory"); + exit(); } + return null; } String leftPad (int val) { @@ -280,7 +323,7 @@ void renderPages() { pageBuffer.image(frameBuffer, leftX, topY, FRAME_W, FRAME_H); } - if (!SOUND.equals("")) { + if (HAS_SOUND) { soundTop = y * SPACING; soundLeft = (x * round(16 * DPMM)) + LEFT_PAD + FRAME_W + round(0.3368 * DPMM); try { @@ -298,9 +341,10 @@ void renderPages() { } } pageBuffer.endDraw(); - pageBuffer.save(RENDER_PATH + "page_" + page + ".tif"); - println("Saved page_" + page + ".tif"); + pageBuffer.save(dataPath("page_" + page + ".tif")); + println("Saved page_" + dataPath(page + ".tif")); } printInfo(); + println("Completed"); exit(); } diff --git a/scripts/calibration.sh b/scripts/calibration.sh index 87c73df..07556e4 100644 --- a/scripts/calibration.sh +++ b/scripts/calibration.sh @@ -7,7 +7,11 @@ #Requires ImageMagick #Printer DPI -DPI=1440 +if [[ "${1}" == "" ]]; then + echo "Please provide your DPI as your first argument" + exit 1 +fi +DPI=${1} #Location of calibration files CALIBRATION_FILES="../filmless_calibration/*.tif" diff --git a/scripts/export.sh b/scripts/export.sh index 4a50998..50557ef 100755 --- a/scripts/export.sh +++ b/scripts/export.sh @@ -23,8 +23,8 @@ if [ ! -f "${VIDEO}" ]; then fi # change these to directory where you will store your frames and audio -FRAMES_DIR=~/Desktop/frames/ -AUDIO_DIR=~/Desktop/audio/ +FRAMES_DIR=./filmless_processing/data/frames/ +AUDIO_DIR=./filmless_processing/data/audio/ mkdir -p "$FRAMES_DIR" mkdir -p "$AUDIO_DIR" @@ -32,7 +32,7 @@ mkdir -p "$AUDIO_DIR" echo "Exporting ${VIDEO}..." -rm "${FRAMES_DIR}*.png" +rm -f "${FRAMES_DIR}*.png" ffmpeg -y -i "${VIDEO}" -f image2 -r 24 -compression_algo raw -pix_fmt rgb24 -crf 0 "${FRAMES_DIR}image-%08d.png" if [ "$WITH_SOUND" == "true" ]; then diff --git a/scripts/pages.sh b/scripts/pages.sh index 9b42635..f699264 100644 --- a/scripts/pages.sh +++ b/scripts/pages.sh @@ -6,15 +6,19 @@ #Requires ImageMagick #Printer DPI, same as in filmless_processing.pde -DPI=1440 +if [[ "${1}" == "" ]]; then + echo "Please provide your DPI as your first argument" + exit 1 +fi +DPI=${1} #Location of generated pages -PAGE_FILES="~/Desktop/page_*.tif" +PAGE_FILES="./filmless_processing/data/page_*.tif" echo "Changing exported page files to ${DPI}dpi..." for f in $PAGE_FILES do name=$(basename "$f" .tif) - echo "Converting ${f} to ~/Desktop/${name}.png @ ${DPI}dpi..." - convert $f -units PixelsPerInch -density $DPI "~/Desktop/${name}.png" + echo "Converting ${f} to ./filmless_processing/data/${name}.png @ ${DPI}dpi..." + convert $f -units PixelsPerInch -density $DPI "./filmless_processing/data/${name}.png" done \ No newline at end of file