最近博主閑下來(lái)了,思考人生接下來(lái)的方向,無(wú)聊時(shí)幫別人做了點(diǎn)小東西,貢獻(xiàn)出來(lái):
jre-17.0.7_win-x64的生成方式:
以管理員方式運(yùn)行PowerShell執(zhí)行命令:
cd $env:JAVA_HOME
jlink --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules java.base,java.xml,java.desktop,jdk.management.agent --output E:\jre-17.0.7_win-x64
內(nèi)部參數(shù):
java -Dfile.encoding=UTF-8 -jar single-subtitle-text-1.0.0.jar
使用教程:
注意該圖片的兩個(gè)文件夾,都在下面這個(gè)軟件的安裝目錄中。
VideoSubFinderWXW 拿來(lái)做字幕圖片截取捕獲,這是下載地址:
VideoSubFinder download | SourceForge.net
下載安裝完成后,步驟:打開(kāi)視頻框選字幕位置,清空所有圖片(之前識(shí)別的圖片),開(kāi)始識(shí)別。
捕獲完在安裝目錄圖片所示的第一個(gè)文件夾RGBImages中,PearOCR可以安裝成瀏覽器應(yīng)用可以離線識(shí)別,pearOCR官網(wǎng)如下:
PearOCR,在線圖片轉(zhuǎn)文字,免費(fèi)OCR,在線圖片文字提取,本地運(yùn)算,無(wú)上傳
上傳RGBImages所有圖片識(shí)別完后導(dǎo)出為txt。(如果后續(xù)出現(xiàn)問(wèn)題,文件路徑及文件名不建議帶有中文)
接下來(lái)是我開(kāi)發(fā)的小工具(代碼已開(kāi)源到github上),下載地址如下,
Release single-subtitle-text · LCJamI/single-subtitle-text (github.com)
下載single-subtitle-text_win-x64.zip解壓,看到single-subtitle-text_win-x64.exe。
安全無(wú)毒,下面是代碼:
package pers.lcj.tool;
import lombok.extern.log4j.Log4j2;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Aaron on 2023/4/28 0:29
*/
@Log4j2
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in, "GBK");
System.out.println("請(qǐng)輸入導(dǎo)入文件路徑:");
Path inputPath = getPath(scanner, "導(dǎo)入文件");
System.out.println("請(qǐng)輸入導(dǎo)出目錄路徑:");
Path outputPath = getPath(scanner, "導(dǎo)出目錄");
Map<String, String> result = readFile(inputPath.toString());
writeOutputFiles(outputPath, result);
}
public static Path getPath(Scanner sc, String pathType) {
Path path;
do {
String inputPath = sc.nextLine().replaceAll("^\"+|\"+$", "");
path = Paths.get(inputPath);
if (pathType.equals("導(dǎo)入文件") && !Files.isRegularFile(path)) {
System.out.println("輸入路徑不是一個(gè)有效文件路徑,請(qǐng)重新輸入:");
path = null;
} else if (pathType.equals("導(dǎo)出目錄") && !Files.isDirectory(path)) {
System.out.println("輸入路徑不是一個(gè)有效文件路徑,請(qǐng)重新輸入:");
path = null;
}
} while (path == null);
return path;
}
private static final int BUFFER_SIZE = 8192;
private static final Pattern REGEX_PATTERN = Pattern.compile("(?<=-\\s)([^.]+)(?=\\.jpeg)");
private static Map<String, String> readFile(String inputFilePath) {
Map<String, String> result = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(inputFilePath), BUFFER_SIZE)) {
StringBuilder currentValueBuilder = new StringBuilder(1024); // 初始化StringBuilder大小,避免過(guò)多擴(kuò)容
Matcher matcher;
String key = null;
String currentLine;
while ((currentLine = br.readLine()) != null) {
matcher = REGEX_PATTERN.matcher(currentLine);
if (matcher.find()) {
if (key != null) {
String value = currentValueBuilder.toString().trim();
result.put(key, value.isEmpty() ? "null" : value); // 添加新的結(jié)果到Map中,并清空StringBuilder
currentValueBuilder.setLength(0);
}
key = matcher.group(1);
} else if (key != null) { // 對(duì)currentValue進(jìn)行構(gòu)建
currentValueBuilder.append(currentLine.trim()).append(" "); // 添加空格,避免拼接相鄰字符串時(shí)出現(xiàn)語(yǔ)義混淆
}
}
if (key != null) { // 處理最后一個(gè)結(jié)果
result.put(key, currentValueBuilder.toString().trim().replace(" ------------ prower by PearOCR.com ------------", ""));
}
} catch (IOException e) {
log.error("Error reading file:", e);
}
return result;
}
private static void writeOutputFiles(Path outputFolderPath, Map<String, String> result) {
System.out.println("正在處理,請(qǐng)稍等......");
result.entrySet().parallelStream().forEach(entry -> {
String fileName = entry.getKey();
Path txtFilePath = outputFolderPath.resolve(fileName + ".txt");
try (BufferedWriter bw = Files.newBufferedWriter(txtFilePath, StandardCharsets.UTF_8)) {
bw.write(entry.getValue());
} catch (IOException e) {
log.error("Error write file:", e);
}
});
System.out.println("文件總計(jì)(個(gè)):" + result.size());
}
}
Gradle 8.1.1引入的依賴(lài):
plugins {
// Java插件,該插件提供了Java應(yīng)用和庫(kù)的基本功能
id 'java'
// lombok插件,該插件支持在Java項(xiàng)目中使用Lombok注解
id 'io.freefair.lombok' version '8.0.1'
}
group = 'pers.lcj.tool'
version = '1.0.0'
repositories {
mavenLocal {
url 'file:///D:/m2/repository'
}
maven { url 'https://maven.aliyun.com/repository/public/' }
mavenCentral()
}
dependencies {
// Lombok
compileOnly 'org.projectlombok:lombok:1.18.26'
annotationProcessor 'org.projectlombok:lombok:1.18.26'
// Log4j2
implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.20.0'
// JUnit 5
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3'
}
jar {
duplicatesStrategy = 'exclude'
from {
configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
manifest {
attributes 'Main-Class': 'pers.lcj.tool.Main'
}
}
test {
useJUnitPlatform()
}
log4j2.properties log4j2 配置文件
# 設(shè)置日志級(jí)別為info
rootLogger.level = info
# 控制臺(tái)Appender的配置
appender.console.type = Console
appender.console.name = consoleLogger
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# 根日志記錄器引用控制臺(tái)Appender
rootLogger.appenderRef.stdout.ref = consoleLogger
# 日志文件存放路徑
property.basePath = logs
# 組件Appender的名稱(chēng)、模式、路徑和滾動(dòng)策略
appender.rolling.type = RollingFile
appender.rolling.name = fileLogger
appender.rolling.fileName= ${basePath}/sst.log
appender.rolling.filePattern= ${basePath}/sst_%d{yyyyMMdd}.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%l] - %msg%n
appender.rolling.policies.type = Policies
# 組件Appender的滾動(dòng)策略,包括按大小滾動(dòng)和按時(shí)間滾動(dòng)兩種策略。
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
# 按大小滾動(dòng)策略大小限制
appender.rolling.policies.size.size = 310MB
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
# 按時(shí)間滾動(dòng)策略間隔時(shí)間(單位:天)
appender.rolling.policies.time.interval = 1
# 按時(shí)間滾動(dòng)策略是否調(diào)整時(shí)區(qū)
appender.rolling.policies.time.modulate = true
# 組件Appender的滾動(dòng)策略,包括按大小滾動(dòng)和按時(shí)間滾動(dòng)兩種策略。
appender.rolling.strategy.type = DefaultRolloverStrategy
# 組件Appender的刪除策略。
appender.rolling.strategy.delete.type = Delete
# 刪除文件所在目錄路徑。
appender.rolling.strategy.delete.basePath = ${basePath}
# 刪除最舊的文件之前可以在目錄中保留的最大文件數(shù)。默認(rèn)值為0,這意味著不會(huì)刪除任何文件。
appender.rolling.strategy.delete.maxDepth = 92
# 用于指定刪除文件的條件類(lèi)型,按最后修改時(shí)間刪除。
appender.rolling.strategy.delete.ifLastModified.type = IfLastModified
# 刪除所有早于92天的文件。
appender.rolling.strategy.delete.ifLastModified.age = 92d
# 配置根日志記錄器,引用組件Appender。
rootLogger.appenderRef.rolling.ref = fileLogger
打開(kāi)single-subtitle-text_win-x64.exe,根據(jù)提示輸入該txt文本文件路徑和輸入安裝目錄圖片所示的第二個(gè)文件夾TXTResults的路徑,生成相應(yīng)的TXTResults后
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-740693.html
點(diǎn)擊軟件ocr菜單中的這個(gè)即可生成你想要的視頻字幕了,貢獻(xiàn)給所有做視頻的朋友,市面上做這個(gè)的基本都收費(fèi),特別視頻多且視頻時(shí)長(zhǎng) 長(zhǎng)的工作,可以帶來(lái)助力,大大縮短工作時(shí)長(zhǎng),但畢竟ocr識(shí)別不是完全準(zhǔn)確的,還是需要一些人工審核。雖然意義不是很大但是對(duì)同聲傳譯的視頻提取字幕,新聞傳播類(lèi)等的二次制作等有幫助,以及博主不知道的領(lǐng)域等有幫助。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-740693.html
到了這里,關(guān)于視頻硬字幕提取方法(可完全離線),開(kāi)發(fā)個(gè)小工具輔助一下的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!