Updates to scripts, sketches and file structures to better support Windows and non-English systems. Move all data to "data" dir within filmless_processing sketch.

This commit is contained in:
mmcwilliams 2024-04-24 09:47:52 -06:00
parent c38231b155
commit c5aecd3053
9 changed files with 98 additions and 60 deletions

10
.gitignore vendored
View File

@ -1 +1,11 @@
*.DS_Store *.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

View File

@ -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à. 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. 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 ### 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. 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. There are variables at the beginning of the sketch that you will need to change to properly generate pages from your images.
```java ```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. 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 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.
```java ```java
String SOUNDTRACK_TYPE = "unilateral"; 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). 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 ```java
String PITCH = "long"; String PITCH = "long";
``` ```

View File

View File

@ -10,14 +10,9 @@ import soundtrack.optical.*;
* CHANGE THESE * 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 //types: unilateral, variable area, dual variable area, maurer, variable density
String SOUNDTRACK_TYPE = "unilateral"; int DPI = 1200; //maximum printer DPI
int DPI = 1440; //maximum printer DPI String SOUNDTRACK_TYPE = "unilateral";
String PITCH = "long"; // long, short //7.62, 7.605 String PITCH = "long"; // long, short //7.62, 7.605
String FORMAT = "16mm"; //16mm or super16 String FORMAT = "16mm"; //16mm or super16
int PERFS = 1; //single (1) or double (2) perf film 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); color PERFS_COLOR = color(255);
int SOUND_OFFSET = 25; 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 //This is a magic number that is used to scale the vertical (H) or horizontal (W) resolution
//because the printer sometimes lies to you. //because the printer sometimes lies to you.
float MAGIC_H_CORRECTION = 1.0; float MAGIC_H_CORRECTION = 1.0;
@ -62,6 +63,8 @@ int FRAME_LINE = round((SPACING - FRAME_H) / 2);
int PAGES = 0; int PAGES = 0;
int FRAMES = 0; int FRAMES = 0;
int SOUND_W = ceil(DPMM * (12.52 - 10.26)); int SOUND_W = ceil(DPMM * (12.52 - 10.26));
boolean HAS_SOUND = false;
String SOUNDTRACK_FILE = "";
SoundtrackOptical soundtrack; SoundtrackOptical soundtrack;
String[] frames; String[] frames;
@ -74,27 +77,29 @@ void setup () {
size(640, 480); size(640, 480);
//surface.setResizable(true); //surface.setResizable(true);
println(SOURCE); println(SOURCE);
frames = listFrames(SOURCE); println(SOUND);
frames = listFrames(SOURCE, SOUND);
if (frames == null) { if (frames == null) {
println("Frames not found, check SOURCE path"); println("Frames not found, check SOURCE path");
exit(); exit();
return;
} }
FRAMES = frames.length; FRAMES = frames.length;
PAGES = ceil((float) FRAMES / (ROWS * COLUMNS)); PAGES = ceil((float) FRAMES / (ROWS * COLUMNS));
pageBuffer = createGraphics(PAGE_W_PIXELS, PAGE_H_PIXELS); pageBuffer = createGraphics(PAGE_W_PIXELS, PAGE_H_PIXELS);
if (!SOUND.equals("")) { if (HAS_SOUND) {
soundtrack = new SoundtrackOptical(this, SOUND, DPI, 1.0, SOUNDTRACK_TYPE, PITCH, !NEGATIVE); soundtrack = new SoundtrackOptical(this, SOUNDTRACK_FILE, DPI, 1.0, SOUNDTRACK_TYPE, PITCH, !NEGATIVE);
} }
printInfo(); 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?"); 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?"); println("WARNING: Super16 frame and soundtrack will interfere with one another. Are you sure?");
} }
@ -139,23 +144,33 @@ void printInfo() {
} }
} }
String[] listFrames (String dir) { String[] listFrames (String dir, String audioDir) {
ArrayList<String> tmp = new ArrayList<String>(); ArrayList<String> tmp = new ArrayList<String>();
ArrayList<String> audioTmp = new ArrayList<String>();
String output[]; String output[];
File file; File file;
File audioFile;
int arraySize; int arraySize;
int o = 0; int o = 0;
if (dir.substring(dir.length() - 1, dir.length()) != "/") { dir = dataPath(dir);
dir = 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); file = new File(dir);
audioFile = new File(SOUND);
if (file.isDirectory()) { if (file.isDirectory()) {
String names[] = file.list(); String names[] = file.list();
names = sort(names); names = sort(names);
for (int i = 0; i < names.length; i++) { 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(".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")) { names[i].toLowerCase().contains(".png")) {
tmp.add(dir + names[i]); tmp.add(dir + names[i]);
} }
@ -163,13 +178,39 @@ String[] listFrames (String dir) {
arraySize = tmp.size(); arraySize = tmp.size();
if (!SOUND.equals("")) { 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 (HAS_SOUND) {
arraySize += SOUND_OFFSET; arraySize += SOUND_OFFSET;
} }
output = new String[arraySize]; output = new String[arraySize];
if (!SOUND.equals("")) { if (HAS_SOUND) {
for (int i = 0; i < SOUND_OFFSET; i++) { for (int i = 0; i < SOUND_OFFSET; i++) {
output[o] = "_BLANK_"; output[o] = "_BLANK_";
o++; o++;
@ -183,8 +224,10 @@ String[] listFrames (String dir) {
sort(output); sort(output);
return output; return output;
} else { } else {
return null; println("ERROR: SOURCE variable does not point to a directory");
exit();
} }
return null;
} }
String leftPad (int val) { String leftPad (int val) {
@ -280,7 +323,7 @@ void renderPages() {
pageBuffer.image(frameBuffer, leftX, topY, FRAME_W, FRAME_H); pageBuffer.image(frameBuffer, leftX, topY, FRAME_W, FRAME_H);
} }
if (!SOUND.equals("")) { if (HAS_SOUND) {
soundTop = y * SPACING; soundTop = y * SPACING;
soundLeft = (x * round(16 * DPMM)) + LEFT_PAD + FRAME_W + round(0.3368 * DPMM); soundLeft = (x * round(16 * DPMM)) + LEFT_PAD + FRAME_W + round(0.3368 * DPMM);
try { try {
@ -298,9 +341,10 @@ void renderPages() {
} }
} }
pageBuffer.endDraw(); pageBuffer.endDraw();
pageBuffer.save(RENDER_PATH + "page_" + page + ".tif"); pageBuffer.save(dataPath("page_" + page + ".tif"));
println("Saved page_" + page + ".tif"); println("Saved page_" + dataPath(page + ".tif"));
} }
printInfo(); printInfo();
println("Completed");
exit(); exit();
} }

View File

@ -7,7 +7,11 @@
#Requires ImageMagick #Requires ImageMagick
#Printer DPI #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 #Location of calibration files
CALIBRATION_FILES="../filmless_calibration/*.tif" CALIBRATION_FILES="../filmless_calibration/*.tif"

View File

@ -23,8 +23,8 @@ if [ ! -f "${VIDEO}" ]; then
fi fi
# change these to directory where you will store your frames and audio # change these to directory where you will store your frames and audio
FRAMES_DIR=~/Desktop/frames/ FRAMES_DIR=./filmless_processing/data/frames/
AUDIO_DIR=~/Desktop/audio/ AUDIO_DIR=./filmless_processing/data/audio/
mkdir -p "$FRAMES_DIR" mkdir -p "$FRAMES_DIR"
mkdir -p "$AUDIO_DIR" mkdir -p "$AUDIO_DIR"
@ -32,7 +32,7 @@ mkdir -p "$AUDIO_DIR"
echo "Exporting ${VIDEO}..." 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" 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 if [ "$WITH_SOUND" == "true" ]; then

View File

@ -6,15 +6,19 @@
#Requires ImageMagick #Requires ImageMagick
#Printer DPI, same as in filmless_processing.pde #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 #Location of generated pages
PAGE_FILES="~/Desktop/page_*.tif" PAGE_FILES="./filmless_processing/data/page_*.tif"
echo "Changing exported page files to ${DPI}dpi..." echo "Changing exported page files to ${DPI}dpi..."
for f in $PAGE_FILES for f in $PAGE_FILES
do do
name=$(basename "$f" .tif) name=$(basename "$f" .tif)
echo "Converting ${f} to ~/Desktop/${name}.png @ ${DPI}dpi..." echo "Converting ${f} to ./filmless_processing/data/${name}.png @ ${DPI}dpi..."
convert $f -units PixelsPerInch -density $DPI "~/Desktop/${name}.png" convert $f -units PixelsPerInch -density $DPI "./filmless_processing/data/${name}.png"
done done