Introduction: Digitalizar Una Película De 16 Mm

 Hace algunos meses encontramos una película de 16 mm dentro de un viejo cajón. Había sido tomada por nuestro padre en 1943 o tal vez en 1944. La película está en negativo y por la limitaciones de la guerra de entonces y también por razones económicas, nunca fue positivada y fue olvidada.


Para poder verla hoy día la solución lógica es la digitalización del negativo y luego transformar en positivo ya digitalizado. Esto requiere capturar cada cuadro de la película para llevarlo a un archivo digital.


Sin embargo, el proceso de digitalización en 16mm resulta ser difícil actualmente, ya que el formato está algo obsoleto y se usa poco, lo que no ocurre con los formatos 8 y súper ocho, más populares en los años 60-80. Hoy no es fácil ni barato conseguir un servicio de digitalización para este tipo de film.


Entonces, la solución que desarrollamos ha sido improvisar una especie de digitalizador artesanal que permitiera el proceso, aún cuando fuese lento, ya que es una tarea que si se logra, se hará una sola vez.


Dadas las limitaciones y condiciones que nos impuso la pandemia, el acceso a piezas y partes es muy problemático por lo que se ha debido recurrir a piezas y partes materiales de desecho que suelen haber en casa.


Esperamos que la solución al problema presentado sea útil a otras personas que tengan problemas similares, conscientes que es posible de ser mejorado en muchos aspectos.

 La solución diseñada se centra en un microcomputador Arduino con un shield de relés, un motor paso a paso y un equipo de digitalización de películas con alta resolución. El aparato digitalizador almacena los fotogramas en formato JPG con un nombre que tiene numeración secuencial, lo que es indispensable posteriormente para la generación del archivo de cine.  


Supplies

  1.  Digitalizadora de diapositivas. Aliexpress. High Resolution 22 MP 35mm Negative Film Scanner 110 135 126KPK Super 8 Slide Film Photo Scanner Digital Film Converter 2.4"LCD

Nótese el soporte para película 110 ya que se necesita para guiar bien la película de 16mm.

  1. Arduino UNO R3. Muy conocido.
  2. Two Relay Shield. – Ofrece dos relés y dos entradas. Modelo MCI pero hay alternativas.
  3. Motor stepper y su controlador. Se requiere un motor con bastante torque ya que el digitalizador ofrece cierto roce. Hemos usado el motor 28BYJ-48.
  4. Un Switch on-off.- Se utilizará para una entrada de control de inicio.
  5. Pulsador momentáneo. Será una segunda entrada para control de avance..
  6. Soportes mecánicos para los carretes de película, motor y scanner.
  7. Cables y conectores.
  8. Una base sólida donde adherir las componentes.
  9. Mucho adhesivo de silicona del tipo pistola térmica.
  10. Una rueda de engranaje de plástico a modificar. Se usó una rueda de 25 mm de diámetro que se encontró dentro de una impresora descompuesta. Esta rueda dentada hace avanzar la película.  

Step 1: Desarrollo

 No describiré los intentos fracasados ya que exceden un libro.

  • Los soportes de los carretes se realizaron con restos de un juguete, un antiguo equipo de construcción de la marca Fisher Technic. Aunque está discontinuado, existen muchos juguetes similares.
  • Se intervino el scanner para acceder a los botones de captura y almacenaje mediante cables que se conectan a los relés.
  • El soporte del motor se logró con piezas sueltas que se unieron con silicona.
  • Se limaron los dientes de un engranaje dentado de plástico que se adhirió al eje del motor stepper. Se eliminaron 5 dientes de cada 6 de modo que los dientes restantes puedan engranar en las perforaciones de la película. No resultó una división exacta, lo que significó irregularidades que se corrigieron con el software. Esta pieza es clave y se puede mejorar mediante un diseño para impresora 3D.
  • Se intentó hacer un sistema de enrollado de la película ya procesada pero resultó muy complejo por lo que se enrolló manualmente cada cierto lapso.
  • Un trozo de espuma plástica sujetado sobre la rueda engranaje permite mantener el arrastre en buena forma. Un par de monedas (16 gr.) proporcionó la presión necesaria.
  • Junto a los relés se instaló: un switch para detener el proceso en forma precisa entre dos períodos de trabajo y otro pulsador permite avanzar un paso cada vez para alinear si es necesario

Step 2: Software

 Hay cuatro piezas de software:

  • El programa para el Arduino desarrollado en el IDE Arduino, incluido más abajo.
  • El programa gratis Irfanview para rotar los fotogramas capturados y llevarlos a escala de grises. Eventualmente se puede hacer alguna ecualización cromática si se requiere.
  • Un script en Octave (clon gratis de Matlab) que permite alinear los cuadros según la posición de las perforaciones, incluído en el último punto.
  • El programa libre FFmpeg.exe permite llevar la colección de fotogramas a una película MP4.

El comando usado es:

ffmpeg -framerate 10 -i IMG%4d.jpg pelicula.mp4

El parámetro “IMG%4d.jpg” permite identificar la secuencia numerada de fotogramas, es decir: IMG0001, IMG0002, IMG0003, etc.

Step 3: Comentario

 El proceso no es “rápido” y la paciencia es una componente muy necesaria en este sistema. Cada fotograma demora cerca de 5 segundos con el programa Arduino y se puede optimizar según la velocidad del stepper.

El ajuste del encuadre puede fluctuar según la calidad de las perforaciones registradas en los fotogramas. La estrategia del encuadre realizada en Octave no es muy elaborada por lo que hay margen de optimización para un buen programador.

El ítem más caro es el scanner de negativos (USD 60 aproximadamente) pero puede seguir prestando utilidad una vez terminada la digitalización.

Ha sido muy satisfactorio realizar esta modesta instalación que ha permitido ver imágenes de nuestros seres queridos después de 78 años. Comparto esta experiencia con aegría.

Step 4: Listado Arduino

Este programa puede ser mejorado si se coordinan los tiempos de almacenado y transporte.

/*Step8.ino 2 mayo 2022 Jaime Aravena-Lopez

Movimiento motor 28BYJ-48. Basado en

https://programarfacil.com/blog/motor-paso-a-paso/

con switch 3 se queda en espera. Mientras está en

espera, se puede encuadrar con el pulsador 2

*/

// dato

int NP = 40;// número de steps del motor de avance


// SHIELD relé MCI

#define cap 6 // switch captura en el shield Relé 2

#define guar 7 // switch guardar en el shield rele 1

#define av 2 // entrada pulsador que avanza 1 step. D2 Gnd

#define sw 3 // entrada switch de espera.


// bobinas del motor

#define IN1 8

#define IN2 9

#define IN3 10

#define IN4 11


// Secuencia par menor(/programarfacil.com/blog)

int paso [4][4] =

{ {1, 0, 0, 0},

{0, 1, 0, 0},

{0, 0, 1, 0},

{0, 0, 0, 1}

};


int cuadros = 0; // contado de cuadros leidos


void setup() { ///////////////////////////////

Serial.begin(115200);

Serial.println("\nStep7.ino");

pinMode(sw, INPUT_PULLUP); // switch de espera o continuacion

// Shield

pinMode(av, INPUT_PULLUP); // switch de avance 1 step

pinMode(cap, OUTPUT); // relé captura 6

pinMode(guar, OUTPUT);// rele save 7

// motor

pinMode(IN1, OUTPUT);

pinMode(IN2, OUTPUT);

pinMode(IN3, OUTPUT);

pinMode(IN4, OUTPUT);

} ////////////////////////////////////////


void loop() { /////////////////////////

while (digitalRead(sw) == 1) {

// bucle de espera para iniciar o detener e proceso.

// dentro de este bucle se puede aumentar el valor de NP

Serial.println(digitalRead(sw)); esperar al switch

if (digitalRead(av) == 0) { // este es el pulsador de un paso

avanza(1);

NP = NP + 1; // se alarga desde ahora

delay(1000); // para debouncing

}

}// se queda en espera si el switch lo indica o avanza un step


// inicio del proceso

avanza(NP); // llamar rutina de avance con NP pasos.

Serial.println("\n avanza un cuadro");

// capturar

digitalWrite(cap, HIGH); // relé de captura

delay(200); // tiempo de conexion

Serial.println(" captura");

digitalWrite(cap, LOW); // soltar el rele de captura

delay(500);// espera para terminar capturar

digitalWrite(guar, HIGH); // rele de almacenar

delay(200);

digitalWrite(guar, LOW); // soltar boton de guardar

Serial.println(" guarda");

delay(1500); // espera a guardar

cuadros = cuadros + 1;

Serial.print(cuadros); Serial.print(" cuadros \n")

}/////////////////////////////////////////


void avanza(int k) { ///////////////

// k = número de pasos

for (int z = 0; z < k; z++) {

for (int i = 0; i < 4; i++) { // un paso en el stepper

digitalWrite(IN1, paso[i][0]);

digitalWrite(IN2, paso[i][1]);

digitalWrite(IN3, paso[i][2]);

digitalWrite(IN4, paso[i][3]);

delay(10);

} // fin for i

} // fin for z

}////////////////////////////////////

Step 5: Scrip Octave

El problema que tenemos que resolver se origina en el hecho que la imagen digitalizada que entrega el scanner, imagen de captura, es de 4008✕5344 pixeles incluye algo más que dos fotogramas de la película, las cuales miden 4008✕2740 pixeles.

Necesitamos entonces recortar y centrar la imagen al tamaño necesario para tener solo un fotograma útil por archivo. Para ello podemos recurrir a la alineación que proveen las perforaciones, que también están digitalizadas.

La calidad de la imagen en la zona de las perforaciones es fundamental para alinear bien y deben ser totalmente negras, lo que se representa por un valor cromático cero.

El programa Octave permite tratar a las imágenes como matrices en que los píxeles entregan dos dimensiones de la matriz y la luminosidad entre cero y 255 es la tercera dimensión.

La estrategia usada es:

Una vez leída la imagen en la matriz I, desde ellarecortamos dos cintas verticales en las matrices C1, C2 de ancho 80 píxeles y que estén en las zonas de perforaciones del fotograma. La imagen de la perforación tiene el valor cero mientras que el resto de la matriz tiene valores que llegan a 255 según la luminosidad.

Si sumamos ambas imágenes, C1 y C2, y luego las escalamos (mediante la función im2bw) para dejar en cero los valores menores que el umbral empírico 0.1, entonces en la zona a la altura de las perforaciones se encontrarán valores nulos. Sin embargo, a veces resulta una decisión equivocada, lo que da origen a saltos en la película. Por ello el valor empírico de escalar la imagen debemos determinar según la calidad del fotograma.

Luego iteramos verticalmente examinando un área de tamaño 4✕80 píxeles en la imagen resultante avanzando (según ‘n’) hasta determinar que efectivamente encontramos una perforación en esa coordenada vertical, por ser una zona con valor nulo.

Una vez que determinamos la posición ‘n’ de la perforación, recortamos la imagen al tamaño de 4008✕2740, desplazándonos para ello en 150 píxeles verticalmente para ubicarnos en el comienzo del cuadro útil.

Con el programa “itera.m” recorremos el directorio cargando cada fotograma y para procesar secuencialmente.  

Archivo Hoyo.m

function S=hoyo(X)

% detecta posición del hoyo en lado izquierdo y derecho.

I=imread(X);

[ro co]=size(I); Tamaño de lo escaneado

cinta=80; % ancho de la cinta a examinar desde col 10 a 80+10

a = co-cinta-10; % para no tomar los bordes mismos

C1 = imcrop(I, [10 1 cinta ro]); % lado izquierdo desde px 10

C2 = imcrop(I, [a 1 cinta ro]); % cinta derecha

Z = imadd(C1,C2); % las zonas ambos cero (hoyo) seguiran en cero

Y = im2bw(Z,0.1); % escala segun un umbral empirico

[r1 c1] = size(Y); % tamaño de la cinta-suma resultante

so=0; % inicializa para asegurar un resultado

for n=1:r1-4 % buscar inicio del hoyo

if max(max(Y(n:n+4, 1:c1)))==0 % revisa una zona alto 4

so=n; % aquí empieza el hoyo aprox

break

endif

endfor

S=imcrop(I, [1 (so+150) 4008 2740]);% crop hasta mitad del hoyo

%imshow(S)

imwrite(S, strcat('C:\TEMP\', X));

endfunction

Archivo itera.m

myFolder = 'C:\Users\ja\Documents\PROYECTO\soft\octave\peli\';

addpath(myFolder)

filepattern = fullfile(myFolder, '*.jpg');

files = dir(filepattern);

for i=1:length(files)% no considera . ni ..

hoyo(files(i).name);

endfor