Импортировать субтитры в обработку

Я работаю над этим эскизом над обработкой, который получает видеопоток с моей веб-камеры/смартфона и показывает его во время работы. Я хочу импортировать в него файл субтитров .srt, преобразованный в txt, из фильма. Я вижу, что в текстовом файле есть все эти числа, которые обозначают начальный и конечный кадры субтитров перед фактическим текстом.

Вот пример:

{9232}{9331}Those swings are dangerous.|Stay off there. I haven't fixed them yet.
{9333}{9374}I think you're gonna live.

Что я хотел бы сделать, так это выяснить код, который будет

  1. используйте числа и установите их в качестве начального/конечного кадров, чтобы они запускались в нужное время, как в фильме
  2. отображать субтитры
  3. выясните, как знак '|' можно использовать в качестве символа для запуска в сценарии смены строки.

Я предполагаю, что это может быть уже довольно сложно, но я просто хотел проверить, делал ли кто-нибудь что-то подобное в прошлом.

Я думаю, что я хочу сделать, это спасти меня от всего

if ((current_frame > 9232) && ((current_frame < 9331)) {
    text("Those swings are dangerous.", 200, 500/2);
    text("Stay off there. I haven't fixed them yet..", 200, (500/2 + 35));
} 

штука для каждого субтитра...

Я новичок в обработке, поэтому не знаком со многими командами, кроме «для» и «если», новичок в импорте файлов .txt и невежда в работе с массивами. Но я действительно хочу найти хороший способ в последних двух битах.

Любая помощь в любой форме будет принята с благодарностью :)

Привет, Джордж


person user3015226    schedule 20.11.2013    source источник


Ответы (1)


Для отображения соответствующих субтитров вы можете сделать что-то вроде следующего (пояснение ниже, заранее извините за стену текста):

String[] subtitles = loadStrings("subtitles.txt");
int currentFrame = 0;
int subtitleIndex = -1;
int startFrame = -1, endFrame = -1;
int fontSize = 10; //change to suit your taste
String[] currentSubtitle;
...

//draw loop start:
//video drawing code goes here

if(currentFrame > endFrame){ //update which subtitle is now/next
  subtitleIndex++;
  startFrame = int(subtitles[subtitleIndex].split("\\}\\{")[0].substring(1));
  endFrame = int(subtitles[subtitleIndex].split("\\}\\{")[1].split("\\}")[0]);
  currentSubtitle = subtitles[subtitleIndex].split("\\}")[2].split("\\|");
}
if(currentFrame >= startFrame && currentFrame <= endFrame){
  for(int i = 0; i < currentSubtitle.length; i++){
    text(currentSubtitle[i], width/2, height - fontSize * (currentSubtitle.length - i));
  }
}
currentFrame++;
//draw loop end

Возможно, это выглядит довольно пугающе для вас, так что вот небольшой комментарий. Ваша программа будет типом машины состояний. Он либо будет в состоянии отображения субтитров, либо нет. Мы учтем это позже, когда будем разрабатывать код. Во-первых, вам нужно объявить и инициализировать ваши переменные.

В первой строке используется функция loadStrings(), которая считывает текстовый файл и возвращает массив String. где каждый элемент массива является строкой в ​​файле. Конечно, вам нужно будет изменить имя файла, чтобы оно соответствовало вашему файлу.

В вашем коде используется переменная с именем current_frame, что является очень хорошей идеей, но я переименовал ее в currentFrame, чтобы она соответствовала соглашению о кодировании Java. Мы начнем с нуля, а позже наш код будет увеличивать его при каждом отображении кадра. Эта переменная сообщит нам, где мы находимся в последовательности субтитров и какое сообщение должно отображаться (если есть).

Поскольку информация о том, в каком кадре начинается и заканчивается каждый субтитр, закодирована в виде строки, включить ее в код довольно сложно. А пока давайте просто создадим несколько переменных, которые представляют, когда «текущий» субтитр — субтитр, который мы отображаем в данный момент или будем отображать в следующий раз, — начинается и заканчивается. Мы также создадим индекс, чтобы отслеживать, какой элемент в массиве subtitles является «текущим» подзаголовком. Все эти переменные начинаются с -1, что может показаться немного странным. Хотя мы инициализировали currentFrame значением 0, на самом деле они не имеют реального «начального» значения, по крайней мере, на данный момент. Если мы выбрали 0, то это не совсем так, потому что первый подзаголовок может не начинаться (вероятно, не должен) в кадре 0, а любое другое положительное число не имеет особого смысла. -1 часто используется в качестве фиктивного индекса, который будет заменен до того, как переменная действительно будет использована, поэтому мы сделаем это и здесь.

Теперь последняя переменная: currentSubtitle. Непосредственной мыслью было бы, чтобы это был простой массив String, а не String. Однако, поскольку каждый подзаголовок, возможно, придется разбить на символы вертикальной черты (|), каждый подзаголовок может фактически представлять несколько строк текста, поэтому мы создадим массив на всякий случай. Возможно, что некоторые субтитры могут быть одноэлементным массивом, но это нормально.

Теперь самое сложное!

Предположительно, в вашем коде будет своего рода цикл, в котором на каждой итерации соответствующий видеокадр отрисовывается на экране и (если условия соблюдены) поверх него отрисовывается субтитр. Я пропустил часть видео, так как это не является частью вашего вопроса.

Прежде чем мы сделаем что-нибудь еще, мы должны помнить, что некоторые из наших переменных еще не имеют реальных значений - все эти -1 должны быть установлены на что-то. Основная логика цикла рисования такова: 1) выяснить, нужно ли рисовать субтитр, и если да, то нарисовать его, и 2) выяснить, нужно ли переместить «текущий» субтитр на следующий в массиве. Давайте сначала выполним № 2, потому что при первом проходе по циклу мы еще ничего о нем не знаем! Критерием (в общем) для перехода к следующему субтитру является то, что мы прошли конец текущего: currentFrame > endFrame. Если это так, то нам нужно переместить все наши переменные к следующему субтитру. subtitleIndex легко, мы просто добавляем один и готово. Остальные... не так просты. Я знаю, это выглядит отвратительно, но я расскажу об этом в конце, чтобы не прерывать поток. Пропустите вперед, если вы просто не можете ждать :)

После (при необходимости) изменения всех переменных так, чтобы они соответствовали текущему субтитру, нам нужно выполнить некоторое фактическое отображение. Второй оператор if проверяет, находимся ли мы «внутри» границ кадра текущего субтитра. Поскольку переменная currentSubtitle может ссылаться либо на субтитр, который нужно отобразить ПРЯМО СЕЙЧАС, либо просто на следующий в последовательности, нам нужно выполнить некоторую проверку, чтобы определить, какой именно субтитр относится к данному кадру. Это второй оператор if -- если мы прошли начало и до конца, то мы должны отображать субтитры! Напомним, что наша переменная currentSubtitle представляет собой массив, поэтому мы не можем просто отобразить ее напрямую. Нам нужно перебрать его и отобразить каждый элемент в отдельной строке. Вы упомянули команду text(), поэтому я не буду вдаваться в подробности. Хитрый бит - это координата y текста, поскольку он должен быть на нескольких строках. Мы хотим, чтобы первый элемент был выше второго, который выше третьего и т. д. Для этого у нас будет координата y, зависящая от того, на каком элементе мы находимся, отмеченная i. Мы можем масштабировать разницу между строками, изменив значение fontSize; это будет только на ваш вкус. Знайте, что число, которое вы установите, будет равно высоте строки в пикселях.

Теперь о запутанной части, которую я не хотел объяснять выше. Этот код зависит от String. ">split(), который выполняется со строкой, которую вы хотите разделить, и принимает строку в качестве параметра, который указывает, как разделить строку - метод regex. Чтобы получить startFrame из строки субтитров в файле, нам нужно разбить его по фигурным скобкам, потому что это разделители между числами. Во-первых, мы разделим строку везде, где встречается "}{" - сразу после первого числа (и прямо перед вторым). Поскольку split() возвращает массив, мы можем ссылаться на одну строку из него, используя индекс между квадратными скобками. Мы знаем, что первое число будет в первой строке, возвращаемой путем разделения на "}{", поэтому мы будем использовать индекс 0. Это вернет (например) "{1234", потому что split() удаляет то, что вы разделяете. Теперь нам нужно просто взять подстрока, расположенная после первого символа, преобразуйте ее в тип int с помощью int() , и мы закончили!

Для второго числа мы можем применить аналогичный подход. Давайте снова разделимся на "}{", только на этот раз мы возьмем второй элемент (индекс 1) в возвращаемом массиве. Теперь у нас есть что-то вроде "9331}Эти колебания чертовски...", которые мы можем снова разделить на "}", выбрать первую строку этого массива, преобразовать в int, и мы снова сделано! В обоих этих случаях мы используем subtitles[subtitleIndex] в качестве исходной строки, которая представляет необработанный ввод файла, который мы загрузили, используя loadStrings() в начале. Обратите внимание, что во время всего этого разбиения исходная строка в subtitles никогда не изменяется — split(), substring() и т. д. возвращают только новые последовательности и не изменяют строку, для которой вы ее вызвали.

Я оставлю это вам, чтобы понять, как работает последняя строка в этой последовательности :)

Наконец, вы увидите, что есть куча обратных косых черт, загромождающих вызовы split(). Это потому, что split() принимает регулярное выражение, а не простую строку. Регулярные выражения используют множество специальных обозначений, которые я не буду здесь рассматривать, но если вы просто передадите split() что-то вроде "}{", оно попытается интерпретировать его и не будет вести себя так, как ожидалось. Вам нужно экранировать символы, говоря split(), что вы не хотите, чтобы они интерпретировались как особые а вам просто нужны сами персонажи. Для этого вы используете обратную реакцию перед любым символом, который необходимо экранировать. Тем не менее, обратная косая черта сама по себе является еще одним специальным символом, поэтому вам тоже нужно избегать его! Это приводит к таким вещам, как "\\{" - первая обратная косая черта экранирует вторую, которая экранирует любой третий символ. Обратите внимание, что символ | также необходимо экранировать.

Извините за стену текста! Приятно видеть, что вопросы задаются грамотно и вежливо, поэтому я подумал, что дам взамен хороший ответ.

person kevinsa5    schedule 21.11.2013
comment
Фантастический ответ! - person mdomino; 28.11.2017