跳到主要内容HarmonyOS 视频关键帧提取与智能体图像分析实战 | 极客日志TypeScriptAI大前端算法
HarmonyOS 视频关键帧提取与智能体图像分析实战
综述由AI生成对 HarmonyOS 应用开发中视频封面生成的痛点,提供了从 AVImageGenerator 实例复用优化、基于 FOCUS 思想的关键帧智能筛选策略、可配置抽帧系统设计到 AI 智能体集成调试的全流程解决方案。涵盖性能优化、内存管理、自适应采样及 API 健壮性封装等关键技术点,帮助开发者构建高效稳定的视频处理系统。
忘忧22 浏览 引言:视频封面生成的技术挑战
在 HarmonyOS 应用开发中,视频内容处理是一个常见但充满挑战的领域。特别是当需要从视频中自动提取最佳封面时,开发者常常面临以下痛点:
- 性能瓶颈:长视频全帧分析计算成本高
- 准确性不足:均匀抽帧容易错过关键画面
- 智能化缺失:传统算法难以理解画面内容价值
- 用户体验差:手动选择封面增加用户操作成本
本文将从实际开发角度,深入分析 HarmonyOS 视频关键帧提取与智能体图像分析的常见问题及解决方案。
常见问题一:HarmonyOS 中如何高效提取视频帧?
问题描述
开发者在使用 AVImageGenerator 提取视频帧时,常遇到性能问题:提取速度慢、内存占用高、处理长视频时应用卡顿。
解决方案分析
1. 正确的 AVImageGenerator 使用方式
async extractMultipleFramesWrong(timestamps: number[]) {
const frames = [];
for (const timestamp of timestamps) {
const generator = await media.createAVImageGenerator();
generator.fdSrc = this.avFileDescriptor;
const pixelMap = await generator.fetchFrameByTime(timestamp);
frames.push(pixelMap);
generator.release();
}
return frames;
}
async extractMultipleFramesCorrect(timestamps: number[]) {
if (!this.avImageGenerator) {
. = media.();
.. = .;
}
frames = [];
( timestamp timestamps) {
pixelMap = ..(
timestamp,
media..,
{ : , : }
);
frames.(pixelMap);
}
frames;
}
this
avImageGenerator
await
createAVImageGenerator
this
avImageGenerator
fdSrc
this
avFileDescriptor
const
for
const
of
const
await
this
avImageGenerator
fetchFrameByTime
AVImageQueryOptions
AV_IMAGE_QUERY_CLOSEST_SYNC
width
480
height
270
push
return
2. 内存优化策略
class FrameExtractor {
private frameCache: Map<number, PixelMap> = new Map();
private maxCacheSize: number = 10;
async getFrame(timestamp: number): Promise<PixelMap> {
if (this.frameCache.has(timestamp)) {
return this.frameCache.get(timestamp)!;
}
const pixelMap = await this.extractFrame(timestamp);
if (this.frameCache.size >= this.maxCacheSize) {
const firstKey = this.frameCache.keys().next().value;
this.frameCache.delete(firstKey);
}
this.frameCache.set(timestamp, pixelMap);
return pixelMap;
}
clearCache(): void {
this.frameCache.forEach(pixelMap => {
pixelMap.release();
});
this.frameCache.clear();
}
}
3. 异步处理与进度反馈
async extractFramesWithProgress(
timestamps: number[],
onProgress?: (progress: number) => void
): Promise<PixelMap[]> {
const frames: PixelMap[] = [];
const total = timestamps.length;
for (let i = 0; i < total; i++) {
try {
const frame = await this.extractFrame(timestamps[i]);
frames.push(frame);
if (onProgress) {
onProgress(Math.round(((i + 1) / total) * 100));
}
if (i % 5 === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
} catch (error) {
console.error(`提取第${i}帧失败:`, error);
}
}
return frames;
}
关键要点
- 实例复用:避免频繁创建和销毁 AVImageGenerator
- 尺寸控制:指定合适的输出尺寸减少内存占用
- 缓存策略:合理缓存已提取的帧
- 异步处理:使用异步操作避免阻塞主线程
- 错误处理:单帧失败不应中断整个提取过程
常见问题二:如何从大量帧中智能筛选最佳封面?
问题描述
均匀抽帧策略简单但效果差,如何实现智能的关键帧选择算法?
解决方案分析
1. FOCUS 算法思想的应用
FOCUS(Fast Object-Centric Understanding of Scenes)算法的核心思想是'粗粒度探索 - 细粒度利用'的两阶段策略:
class SmartFrameSelector {
async coarseExploration(videoDuration: number): Promise<TimeInterval[]> {
const segmentDuration = 10000;
const segments = Math.ceil(videoDuration / segmentDuration);
const candidateIntervals: TimeInterval[] = [];
for (let i = 0; i < segments; i++) {
const startTime = i * segmentDuration;
const endTime = Math.min((i + 1) * segmentDuration, videoDuration);
const sampleFrames = await this.sampleFramesFromInterval(startTime, endTime, 2);
const potentialScore = await this.quickEvaluate(sampleFrames);
if (potentialScore >= 70) {
candidateIntervals.push({
start: startTime,
end: endTime,
score: potentialScore,
confidence: this.calculateConfidence(sampleFrames.length)
});
}
}
return candidateIntervals.sort((a, b) => b.score - a.score);
}
async fineGrainExploitation(intervals: TimeInterval[]): Promise<FrameCandidate[]> {
const candidates: FrameCandidate[] = [];
for (const interval of intervals.slice(0, 3)) {
const denseFrames = await this.denseSample(interval.start, interval.end, 1000);
for (const frame of denseFrames) {
const detailedScore = await this.detailedEvaluate(frame);
candidates.push({
timestamp: frame.timestamp,
score: detailedScore,
imageData: frame.imageData
});
}
}
return candidates.sort((a, b) => b.score - a.score).slice(0, 5);
}
}
2. 多维度评分体系
interface FrameEvaluation {
timestamp: number;
eventSignificance: number;
compositionAesthetics: number;
informationDensity: number;
emotionalResonance: number;
totalScore: number;
}
class FrameEvaluator {
async evaluateFrame(pixelMap: PixelMap): Promise<FrameEvaluation> {
const eventScore = await this.evaluateEventSignificance(pixelMap);
const compositionScore = await this.evaluateComposition(pixelMap);
const infoScore = await this.evaluateInformationDensity(pixelMap);
const emotionScore = await this.evaluateEmotionalResonance(pixelMap);
const totalScore = eventScore + compositionScore + infoScore + emotionScore;
return {
timestamp: Date.now(),
eventSignificance: eventScore,
compositionAesthetics: compositionScore,
informationDensity: infoScore,
emotionalResonance: emotionScore,
totalScore: totalScore
};
}
private async evaluateEventSignificance(pixelMap: PixelMap): Promise<number> {
return 0;
}
}
3. 自适应采样策略
interface SamplingConfig {
strategy: 'uniform' | 'segment' | 'keypoint';
minFrames: number;
maxFrames: number;
maxProcessingTime: number;
qualityThreshold: number;
}
class AdaptiveSampler {
private config: SamplingConfig = {
strategy: 'segment',
minFrames: 3,
maxFrames: 10,
maxProcessingTime: 5000,
qualityThreshold: 75
};
async selectFrames(videoDuration: number): Promise<number[]> {
const timestamps: number[] = [];
if (videoDuration < 60000) {
return this.uniformSampling(videoDuration, this.config.minFrames);
} else if (videoDuration < 300000) {
return this.segmentSampling(videoDuration);
} else {
return this.keypointSampling(videoDuration);
}
}
private uniformSampling(duration: number, count: number): number[] {
const interval = duration / (count + 1);
return Array.from({ length: count }, (_, i) => Math.floor(interval * (i + 1)));
}
}
关键要点
- 两阶段策略:先粗筛后精挑,平衡效率与准确性
- 多维评分:从事件、构图、信息、情感多角度评估
- 自适应采样:根据视频长度动态调整采样策略
- 置信度评估:考虑采样数量的可靠性
常见问题三:如何配置和优化抽帧策略?
问题描述
不同场景需要不同的抽帧策略,如何设计可配置、可扩展的抽帧系统?
解决方案分析
1. 策略配置管理
export interface FrameExtractionConfig {
strategy: 'uniform' | 'segment' | 'keypoint' | 'adaptive';
outputWidth: number;
outputHeight: number;
quality: number;
uniformInterval?: number;
segmentDuration?: number;
framesPerSegment?: number;
keyPoints?: number[];
minFrames?: number;
maxFrames?: number;
qualityThreshold?: number;
maxProcessingTime?: number;
enableCache?: boolean;
cacheSize?: number;
enableAIEvaluation?: boolean;
aiModel?: string;
confidenceThreshold?: number;
}
export const DEFAULT_CONFIG: FrameExtractionConfig = {
strategy: 'adaptive',
outputWidth: 480,
outputHeight: 270,
quality: 85,
segmentDuration: 10000,
framesPerSegment: 2,
minFrames: 3,
maxFrames: 8,
qualityThreshold: 70,
maxProcessingTime: 3000,
enableCache: true,
cacheSize: 20,
enableAIEvaluation: true,
aiModel: 'default',
confidenceThreshold: 0.6
};
export const CONFIG_PRESETS = {
SHORT_VIDEO: {
strategy: 'uniform',
uniformInterval: 3000,
minFrames: 5,
maxFrames: 10,
enableAIEvaluation: false
} as FrameExtractionConfig,
TUTORIAL: {
strategy: 'keypoint',
keyPoints: [0, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99],
enableAIEvaluation: true,
aiModel: 'tutorial'
} as FrameExtractionConfig,
LIVE_REPLAY: {
strategy: 'segment',
segmentDuration: 30000,
framesPerSegment: 1,
enableAIEvaluation: true,
aiModel: 'live'
} as FrameExtractionConfig
};
2. 策略工厂模式
interface FrameExtractionStrategy {
extractFrames(videoUri: string, config: FrameExtractionConfig): Promise<FrameData[]>;
getStrategyName(): string;
}
class UniformStrategy implements FrameExtractionStrategy {
async extractFrames(videoUri: string, config: FrameExtractionConfig): Promise<FrameData[]> {
const videoInfo = await this.getVideoInfo(videoUri);
const interval = config.uniformInterval || 3000;
const frameCount = Math.min(
Math.floor(videoInfo.duration / interval),
config.maxFrames || 10
);
const frames: FrameData[] = [];
for (let i = 0; i < frameCount; i++) {
const timestamp = i * interval;
const frame = await this.extractFrameAtTime(videoUri, timestamp, config);
frames.push(frame);
}
return frames;
}
getStrategyName(): string {
return 'uniform';
}
}
class FrameExtractionFactory {
private strategies: Map<string, FrameExtractionStrategy> = new Map();
constructor() {
this.registerStrategy('uniform', new UniformStrategy());
this.registerStrategy('segment', new SegmentStrategy());
this.registerStrategy('keypoint', new KeypointStrategy());
this.registerStrategy('adaptive', new AdaptiveStrategy());
}
registerStrategy(name: string, strategy: FrameExtractionStrategy): void {
this.strategies.set(name, strategy);
}
getStrategy(config: FrameExtractionConfig): FrameExtractionStrategy {
const strategy = this.strategies.get(config.strategy);
if (!strategy) {
throw new Error(`未找到策略:${config.strategy}`);
}
return strategy;
}
autoSelectStrategy(videoInfo: VideoInfo): FrameExtractionConfig {
if (videoInfo.duration < 60000) {
return CONFIG_PRESETS.SHORT_VIDEO;
} else if (videoInfo.tags?.includes('tutorial')) {
return CONFIG_PRESETS.TUTORIAL;
} else if (videoInfo.tags?.includes('live')) {
return CONFIG_PRESETS.LIVE_REPLAY;
} else {
return DEFAULT_CONFIG;
}
}
}
3. 性能监控与调优
class PerformanceMonitor {
private metrics: ExtractionMetrics[] = [];
async monitorExtraction(
task: () => Promise<FrameData[]>,
videoInfo: VideoInfo
): Promise<{ frames: FrameData[], metrics: ExtractionMetrics }> {
const startTime = Date.now();
const startMemory = process.memoryUsage().heapUsed;
try {
const frames = await task();
const endTime = Date.now();
const endMemory = process.memoryUsage().heapUsed;
const metrics: ExtractionMetrics = {
videoDuration: videoInfo.duration,
frameCount: frames.length,
totalTime: endTime - startTime,
memoryUsed: endMemory - startMemory,
averageTimePerFrame: (endTime - startTime) / frames.length,
successRate: 1,
timestamp: Date.now()
};
this.metrics.push(metrics);
this.cleanupOldMetrics();
return { frames, metrics };
} catch (error) {
const endTime = Date.now();
const metrics: ExtractionMetrics = {
videoDuration: videoInfo.duration,
frameCount: 0,
totalTime: endTime - startTime,
memoryUsed: 0,
averageTimePerFrame: 0,
successRate: 0,
timestamp: Date.now(),
error: error.message
};
this.metrics.push(metrics);
throw error;
}
}
getPerformanceReport(): PerformanceReport {
if (this.metrics.length === 0) {
return { averageTime: 0, successRate: 0, suggestions: [] };
}
const avgTime = this.metrics.reduce((sum, m) => sum + m.totalTime, 0) / this.metrics.length;
const successRate = this.metrics.filter(m => m.successRate === 1).length / this.metrics.length;
const suggestions: string[] = [];
if (avgTime > 5000) {
suggestions.push('提取时间过长,建议减少采样帧数或降低输出质量');
}
if (successRate < 0.9) {
suggestions.push('成功率较低,建议检查视频格式兼容性');
}
return {
averageTime: avgTime,
successRate: successRate,
totalOperations: this.metrics.length,
suggestions: suggestions
};
}
}
关键要点
- 配置化设计:支持不同场景的策略配置
- 工厂模式:便于策略扩展和维护
- 性能监控:实时监控提取性能并给出优化建议
- 自动选择:根据视频特征自动选择最佳策略
常见问题四:智能体分析画面的实际集成与调试
问题描述
集成 AI 智能体进行画面分析时,开发者常遇到 API 调用失败、响应慢、结果不准确等问题。
解决方案分析
1. 健壮的 API 调用封装
class AICoverAnalyzer {
private cozeApiUrl: string = 'https://api.coze.cn/v1/chat/completions';
private maxRetries: number = 3;
private timeout: number = 30000;
async analyzeFramesWithRetry(
frames: FrameData[],
options: AnalysisOptions = {}
): Promise<AnalysisResult> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const result = await this.analyzeFrames(frames, options);
console.log(`第${attempt}次尝试成功`);
return result;
} catch (error) {
lastError = error as Error;
console.warn(`第${attempt}次尝试失败:`, error);
if (attempt < this.maxRetries) {
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await this.delay(delay);
}
}
}
throw new Error(`分析失败,已重试${this.maxRetries}次:${lastError?.message}`);
}
private async analyzeFrames(
frames: FrameData[],
options: AnalysisOptions
): Promise<AnalysisResult> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
try {
const prompt = this.buildAnalysisPrompt(frames, options);
const response = await fetch(this.cozeApiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${options.apiKey || this.apiKey}`
},
body: JSON.stringify({
bot_id: options.botId || this.cozeBotId,
user_id: `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
query: prompt,
stream: false,
additional_messages: this.buildAdditionalMessages(frames)
}),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 请求失败:${response.status} - ${errorText}`);
}
const data = await response.json();
return this.parseAnalysisResult(data);
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('请求超时');
}
throw error;
}
}
private buildAnalysisPrompt(frames: FrameData[], options: AnalysisOptions): string {
const basePrompt = `你是一个专业的视频关键帧分析专家。请分析以下${frames.length}个视频帧,并按照以下标准评分:
评分标准(总分 100 分):
1. 事件显著性(35 分):关键动作捕捉、重要时刻
2. 构图与美学(30 分):画面清晰度、构图平衡、色彩
3. 信息量(20 分):传达核心信息的能力
4. 情感共鸣(15 分):引发情感反应的程度
请为每一帧打分,并推荐最适合作为封面的 1-3 帧。
视频信息:
- 总时长:${options.videoDuration || '未知'}ms
- 帧数:${frames.length}
- 用途:${options.useCase || '通用封面'}
分析要求:
${options.requirements || '请提供详细的评分理由和改进建议'}
帧数据:`;
const frameDescriptions = frames.map((frame, index) => {
return `【第${index + 1}帧】
时间戳:${frame.timestamp}ms
图像特征:${frame.metadata?.description || '待分析'}
------------------------`;
}).join('\n\n');
return `${basePrompt}\n\n${frameDescriptions}`;
}
}
2. 结果缓存与降级处理
class AnalysisCache {
private cache: Map<string, { result: AnalysisResult, timestamp: number }> = new Map();
private cacheTTL: number = 3600000;
async getOrAnalyze(
cacheKey: string,
analyzeFn: () => Promise<AnalysisResult>,
useCache: boolean = true
): Promise<AnalysisResult> {
if (useCache) {
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
console.log('使用缓存结果');
return cached.result;
}
}
try {
const result = await analyzeFn();
this.cache.set(cacheKey, { result: result, timestamp: Date.now() });
this.cleanupExpiredCache();
return result;
} catch (error) {
console.error('分析失败:', error);
if (useCache) {
const cached = this.cache.get(cacheKey);
if (cached) {
console.warn('分析失败,使用过期的缓存结果');
return cached.result;
}
}
return this.getDefaultResult();
}
}
private getDefaultResult(): AnalysisResult {
return {
candidateFrames: [],
analysisSummary: {
totalFrames: 0,
analyzedFrames: 0,
averageScore: 0,
confidence: 0,
fallback: true
},
recommendations: ['分析服务暂时不可用,请稍后重试']
};
}
}
3. 批量处理与流量控制
class BatchProcessor {
private queue: Array<{ frames: FrameData[]; resolve: (result: AnalysisResult) => void; reject: (error: Error) => void; }> = [];
private processing: boolean = false;
private maxBatchSize: number = 5;
private delayBetweenBatches: number = 1000;
async analyzeBatch(frames: FrameData[]): Promise<AnalysisResult> {
return new Promise((resolve, reject) => {
this.queue.push({ frames, resolve, reject });
this.processQueue();
});
}
private async processQueue(): Promise<void> {
if (this.processing || this.queue.length === 0) {
return;
}
this.processing = true;
while (this.queue.length > 0) {
const batch = this.queue.splice(0, this.maxBatchSize);
try {
const results = await Promise.allSettled(
batch.map(item => this.processItem(item.frames))
);
results.forEach((result, index) => {
const { resolve, reject } = batch[index];
if (result.status === 'fulfilled') {
resolve(result.value);
} else {
reject(result.reason);
}
});
if (this.queue.length > 0) {
await this.delay(this.delayBetweenBatches);
}
} catch (error) {
console.error('批次处理失败:', error);
batch.forEach(item => {
this.queue.unshift(item);
});
break;
}
}
this.processing = false;
}
private async processItem(frames: FrameData[]): Promise<AnalysisResult> {
const analyzer = new AICoverAnalyzer();
return analyzer.analyzeFramesWithRetry(frames);
}
}
4. 调试与监控
class AnalysisDebugger {
private logs: DebugLog[] = [];
logAnalysis(
frames: FrameData[],
result: AnalysisResult,
metadata: DebugMetadata
): void {
const log: DebugLog = {
timestamp: Date.now(),
frameCount: frames.length,
analysisTime: metadata.analysisTime,
apiResponseTime: metadata.apiResponseTime,
scores: result.candidateFrames.map(f => f.score),
averageScore: result.analysisSummary.averageScore,
confidence: result.analysisSummary.confidence,
error: metadata.error,
cacheHit: metadata.cacheHit
};
this.logs.push(log);
if (this.logs.length > 1000) {
this.logs = this.logs.slice(-500);
}
this.monitorPerformance(log);
}
private monitorPerformance(log: DebugLog): void {
if (log.apiResponseTime > 10000) {
console.warn(`API 响应时间过长:${log.apiResponseTime}ms`);
}
const recentLogs = this.logs.slice(-100);
const errorCount = recentLogs.filter(l => l.error).length;
const errorRate = errorCount / recentLogs.length;
if (errorRate > 0.1) {
console.error(`分析错误率过高:${(errorRate * 100).toFixed(1)}%`);
}
if (log.scores.length > 0) {
const avgScore = log.scores.reduce((a, b) => a + b, 0) / log.scores.length;
if (avgScore < 50) {
console.warn(`平均分数较低:${avgScore.toFixed(1)}`);
}
}
}
exportLogs(format: 'json' | 'csv' = 'json'): string {
if (format === 'csv') {
return this.convertToCSV();
}
return JSON.stringify(this.logs, null, 2);
}
}
关键要点
- 重试机制:实现指数退避重试,提高稳定性
- 缓存策略:减少重复分析,提供降级方案
- 批量处理:优化 API 调用,避免限流
- 全面监控:实时监控性能和质量指标
- 详细日志:便于问题排查和优化
总结:关键帧提取的最佳实践与未来展望
最佳实践总结
- 性能优先
- 复用 AVImageGenerator 实例,避免频繁创建销毁
- 合理控制输出图像尺寸和质量
- 实现帧缓存机制,减少重复提取
- 智能采样
- 采用两阶段采样策略:粗筛 + 精挑
- 根据视频长度动态调整采样密度
- 实现多维度的帧评估体系
- 稳定集成
- 封装健壮的 API 调用,包含重试和超时处理
- 实现结果缓存和降级方案
- 添加全面的监控和日志
- 可配置性
- 提供多种预设配置适应不同场景
- 支持运行时策略切换
- 允许自定义评估算法
常见问题排查指南
| 问题现象 | 可能原因 | 解决方案 |
|---|
| 提取速度慢 | 1. 输出尺寸过大 2. 采样帧数过多 3. 未复用实例 | 1. 降低输出分辨率 2. 减少采样数量 3. 实现实例复用 |
| 内存占用高 | 1. 未及时释放资源 2. 缓存过大 3. 同时处理多个视频 | 1. 及时调用 release() 2. 限制缓存大小 3. 实现队列处理 |
| AI 分析失败 | 1. API 限流 2. 网络问题 3. 输入数据异常 | 1. 实现重试机制 2. 添加超时处理 3. 验证输入数据 |
| 结果不准确 | 1. 采样策略不当 2. 评估标准不合理 3. 视频内容特殊 | 1. 调整采样策略 2. 优化评估算法 3. 提供人工干预选项 |
未来技术展望
- 端侧 AI 集成
- 利用 HarmonyOS AI 框架实现端侧关键帧分析
- 减少对云端服务的依赖
- 提升分析速度和隐私保护
- 个性化学习
- 基于用户历史选择学习偏好
- 自适应调整评估权重
- 实现越用越准的智能推荐
- 多模态分析
- 结合音频分析识别关键时刻
- 利用字幕文本理解内容重点
- 实现视听融合的智能分析
- 实时处理
- 支持直播流的关键帧提取
- 实现低延迟的实时分析
- 应用于直播封面自动生成
结语
HarmonyOS 视频关键帧提取与智能体图像分析是一个充满挑战但也极具价值的领域。通过本文分析的四类常见问题及解决方案,开发者可以:
- 掌握核心技术:理解 AVImageGenerator 的正确使用方式
- 实现智能算法:应用 FOCUS 思想优化关键帧选择
- 构建健壮系统:设计可配置、可监控的抽帧系统
- 稳定集成 AI:实现与智能体服务的可靠交互
随着 HarmonyOS 生态的不断完善和 AI 技术的快速发展,视频内容理解能力将成为应用差异化竞争的关键。希望本文的分析和解决方案能为开发者在 HarmonyOS 平台上构建优秀的视频处理应用提供有价值的参考。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online