應用場景
在日常工作和生活中,我們經常會遇到需要對大量圖片進行重命名的情況。例如,設計師可能需要根據圖片內容為設計素材命名,文檔管理人員可能需要根據掃描文檔中的文字對圖片進行分類命名。傳統的手動重命名方式效率低下且容易出錯,因此開發一款能夠自動識別圖片中的文字并根據文字內容對圖片進行重命名的工具具有很高的實用價值。
界面設計
我們可以設計一個簡潔易用的 WPF 界面,主要包含以下元素:
- 頂部:應用程序標題和版本信息
- 中部:
- 左側:文件選擇區域,包含文件夾選擇按鈕和已選擇文件列表
- 右側:預覽區域,顯示當前選中的圖片和識別結果
- 底部:操作按鈕區域,包含開始處理、取消和設置按鈕
- 狀態欄:顯示當前處理進度和狀態信息
詳細代碼步驟
以下是實現咕嘎批量OCR識別圖片PDF多區域內容重命名導出表格系統這個功能的詳細代碼步驟:
- 創建 WPF 應用程序項目
- 設計 XAML 界面
- 實現JD?OCR圖片識別功能
- 實現文件處理功能
- 實現界面交互邏輯
下面是完整的代碼實現:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Newtonsoft.Json;
using RestSharp;namespace ImageOcrRenameTool
{/// <summary>/// MainWindow.xaml 的交互邏輯/// </summary>public partial class MainWindow : Window{// 存儲選擇的圖片文件路徑private List<string> _selectedFiles = new List<string>();// 當前選中的圖片索引private int _currentIndex = 0;// 京東OCR配置信息private OcrConfig _ocrConfig = new OcrConfig();// 處理進度private int _processedCount = 0;private int _totalCount = 0;public MainWindow(){InitializeComponent();InitializeConfig();}// 初始化配置private void InitializeConfig(){try{if (File.Exists("config.json")){string json = File.ReadAllText("config.json");_ocrConfig = JsonConvert.DeserializeObject<OcrConfig>(json);}}catch (Exception ex){MessageBox.Show($"加載配置文件失敗: {ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);}}// 保存配置private void SaveConfig(){try{string json = JsonConvert.SerializeObject(_ocrConfig);File.WriteAllText("config.json", json);}catch (Exception ex){MessageBox.Show($"保存配置文件失敗: {ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);}}// 選擇文件夾按鈕點擊事件private void BtnSelectFolder_Click(object sender, RoutedEventArgs e){using (var dialog = new System.Windows.Forms.FolderBrowserDialog()){System.Windows.Forms.DialogResult result = dialog.ShowDialog();if (result == System.Windows.Forms.DialogResult.OK){string folderPath = dialog.SelectedPath;LoadImages(folderPath);}}}// 加載文件夾中的圖片private void LoadImages(string folderPath){try{// 清空現有文件列表_selectedFiles.Clear();lstFiles.Items.Clear();// 獲取文件夾中所有支持的圖片文件string[] imageExtensions = { ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff" };var files = Directory.GetFiles(folderPath).Where(f => imageExtensions.Contains(Path.GetExtension(f).ToLower()));foreach (var file in files){_selectedFiles.Add(file);lstFiles.Items.Add(Path.GetFileName(file));}if (_selectedFiles.Count > 0){_currentIndex = 0;LoadImage(_selectedFiles[_currentIndex]);UpdateStatus($"已加載 {_selectedFiles.Count} 張圖片");}else{UpdateStatus("未找到圖片文件");}}catch (Exception ex){MessageBox.Show($"加載圖片失敗: {ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);}}// 加載單張圖片private void LoadImage(string filePath){try{// 顯示圖片BitmapImage bitmap = new BitmapImage();bitmap.BeginInit();bitmap.CacheOption = BitmapCacheOption.OnLoad;bitmap.UriSource = new Uri(filePath);bitmap.EndInit();imgPreview.Source = bitmap;// 顯示文件名txtFileName.Text = Path.GetFileName(filePath);// 更新文件索引信息lblFileIndex.Text = $"圖片 {_currentIndex + 1} / {_selectedFiles.Count}";}catch (Exception ex){MessageBox.Show($"加載圖片失敗: {ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);}}// 文件列表選擇變更事件private void LstFiles_SelectionChanged(object sender, SelectionChangedEventArgs e){if (lstFiles.SelectedIndex >= 0){_currentIndex = lstFiles.SelectedIndex;LoadImage(_selectedFiles[_currentIndex]);}}// 上一張圖片按鈕點擊事件private void BtnPrev_Click(object sender, RoutedEventArgs e){if (_selectedFiles.Count > 0 && _currentIndex > 0){_currentIndex--;lstFiles.SelectedIndex = _currentIndex;}}// 下一張圖片按鈕點擊事件private void BtnNext_Click(object sender, RoutedEventArgs e){if (_selectedFiles.Count > 0 && _currentIndex < _selectedFiles.Count - 1){_currentIndex++;lstFiles.SelectedIndex = _currentIndex;}}// 開始處理按鈕點擊事件private async void BtnProcess_Click(object sender, RoutedEventArgs e){if (_selectedFiles.Count == 0){MessageBox.Show("請先選擇圖片文件夾", "提示", MessageBoxButton.OK, MessageBoxImage.Information);return;}if (string.IsNullOrEmpty(_ocrConfig.AppKey) || string.IsNullOrEmpty(_ocrConfig.AppSecret)){MessageBox.Show("請先配置京東OCR的AppKey和AppSecret", "提示", MessageBoxButton.OK, MessageBoxImage.Information);ShowSettingsDialog();return;}// 禁用操作按鈕btnSelectFolder.IsEnabled = false;btnProcess.IsEnabled = false;btnSettings.IsEnabled = false;btnPrev.IsEnabled = false;btnNext.IsEnabled = false;// 重置進度_processedCount = 0;_totalCount = _selectedFiles.Count;progressBar.Value = 0;lblProgress.Text = "0%";// 異步處理圖片await ProcessImagesAsync();// 恢復操作按鈕btnSelectFolder.IsEnabled = true;btnProcess.IsEnabled = true;btnSettings.IsEnabled = true;btnPrev.IsEnabled = true;btnNext.IsEnabled = true;}// 異步處理圖片private async Task ProcessImagesAsync(){try{UpdateStatus("開始處理圖片...");for (int i = 0; i < _selectedFiles.Count; i++){string filePath = _selectedFiles[i];_currentIndex = i;// 在UI線程更新文件顯示Dispatcher.Invoke(() =>{lstFiles.SelectedIndex = i;UpdateStatus($"正在處理: {Path.GetFileName(filePath)}");});// 執行OCR識別string ocrResult = await PerformOcrAsync(filePath);// 在UI線程更新識別結果Dispatcher.Invoke(() =>{txtOcrResult.Text = ocrResult;});// 重命名文件if (!string.IsNullOrEmpty(ocrResult)){string newFileName = GenerateNewFileName(ocrResult, filePath);await RenameFileAsync(filePath, newFileName);}// 更新進度_processedCount++;Dispatcher.Invoke(() =>{double progress = (double)_processedCount / _totalCount * 100;progressBar.Value = progress;lblProgress.Text = $"{progress:F0}%";});}UpdateStatus($"處理完成! 共處理 {_processedCount} 張圖片");MessageBox.Show($"處理完成! 共處理 {_processedCount} 張圖片", "完成", MessageBoxButton.OK, MessageBoxImage.Information);}catch (Exception ex){UpdateStatus($"處理失敗: {ex.Message}");MessageBox.Show($"處理失敗: {ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);}}// 執行OCR識別private async Task<string> PerformOcrAsync(string imagePath){try{// 創建RestClient和RestRequestvar client = new RestClient("https://aiapi.jd.com/jdai/ocr_plate");var request = new RestRequest(Method.POST);// 添加請求參數request.AddHeader("Content-Type", "application/x-www-form-urlencoded");request.AddHeader("appkey", _ocrConfig.AppKey);// 讀取圖片文件并轉換為Base64byte[] imageBytes = File.ReadAllBytes(imagePath);string base64Image = Convert.ToBase64String(imageBytes);// 添加請求體request.AddParameter("image", base64Image);request.AddParameter("appsecret", _ocrConfig.AppSecret);// 執行請求IRestResponse response = await client.ExecuteAsync(request);if (response.IsSuccessful){// 解析JSON響應var result = JsonConvert.DeserializeObject<JdOcrResponse>(response.Content);if (result != null && result.code == "0" && result.data != null && result.data.words_result != null){// 提取識別文本string ocrText = string.Join(" ", result.data.words_result.Select(r => r.words));return ocrText;}else{return $"OCR識別失敗: {result?.msg ?? "未知錯誤"}";}}else{return $"請求失敗: {response.StatusCode}";}}catch (Exception ex){return $"識別過程出錯: {ex.Message}";}}// 生成新文件名private string GenerateNewFileName(string ocrText, string originalPath){try{// 移除不允許的文件名字符string invalidChars = new string(Path.GetInvalidFileNameChars());string cleanText = ocrText;foreach (char c in invalidChars){cleanText = cleanText.Replace(c, '_');}// 限制文件名長度if (cleanText.Length > 80){cleanText = cleanText.Substring(0, 80);}// 添加時間戳以確保唯一性string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");string baseName = string.IsNullOrEmpty(cleanText.Trim()) ? "unknown" : cleanText.Trim();string newFileName = $"{baseName}_{timestamp}{Path.GetExtension(originalPath)}";// 獲取文件所在目錄string directory = Path.GetDirectoryName(originalPath);return Path.Combine(directory, newFileName);}catch (Exception){// 如果生成新文件名失敗,使用原始文件名加上時間戳string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");string fileName = Path.GetFileNameWithoutExtension(originalPath);string extension = Path.GetExtension(originalPath);return Path.Combine(Path.GetDirectoryName(originalPath),$"{fileName}_{timestamp}{extension}");}}// 異步重命名文件private async Task RenameFileAsync(string oldPath, string newPath){await Task.Run(() =>{try{// 如果新文件名已存在,則添加序號if (File.Exists(newPath)){string directory = Path.GetDirectoryName(newPath);string fileName = Path.GetFileNameWithoutExtension(newPath);string extension = Path.GetExtension(newPath);int counter = 1;string tempPath;do{tempPath = Path.Combine(directory, $"{fileName}_{counter}{extension}");counter++;} while (File.Exists(tempPath));newPath = tempPath;}// 重命名文件File.Move(oldPath, newPath);// 更新文件列表int index = _selectedFiles.IndexOf(oldPath);if (index >= 0){_selectedFiles[index] = newPath;// 在UI線程更新列表項Dispatcher.Invoke(() =>{if (lstFiles.Items.Count > index){lstFiles.Items[index] = Path.GetFileName(newPath);}});}}catch (Exception ex){Dispatcher.Invoke(() =>{MessageBox.Show($"重命名文件失敗: {ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);});}});}// 設置按鈕點擊事件private void BtnSettings_Click(object sender, RoutedEventArgs e){ShowSettingsDialog();}// 顯示設置對話框private void ShowSettingsDialog(){var settingsWindow = new SettingsWindow(_ocrConfig);if (settingsWindow.ShowDialog() == true){_ocrConfig = settingsWindow.OcrConfig;SaveConfig();}}// 更新狀態欄信息private void UpdateStatus(string message){lblStatus.Text = message;}// 鼠標滾輪事件 - 圖片縮放private void ImgPreview_MouseWheel(object sender, MouseWheelEventArgs e){if (imgPreview.Source != null){double scale = e.Delta > 0 ? 1.1 : 0.9;imgPreview.RenderTransform = new ScaleTransform(scale, scale);imgPreview.RenderTransformOrigin = new Point(0.5, 0.5);}}}// OCR配置類public class OcrConfig{public string AppKey { get; set; }public string AppSecret { get; set; }public bool UseJdOcr { get; set; } = true;public string CustomApiUrl { get; set; }}// 京東OCR響應模型public class JdOcrResponse{public string code { get; set; }public string msg { get; set; }public OcrData data { get; set; }}public class OcrData{public List<WordsResult> words_result { get; set; }}public class WordsResult{public string words { get; set; }}
}
下面是 SettingsWindow 的代碼:
using System.Windows;namespace ImageOcrRenameTool
{/// <summary>/// SettingsWindow.xaml 的交互邏輯/// </summary>public partial class SettingsWindow : Window{public OcrConfig OcrConfig { get; private set; }public SettingsWindow(OcrConfig config){InitializeComponent();OcrConfig = new OcrConfig{AppKey = config.AppKey,AppSecret = config.AppSecret,UseJdOcr = config.UseJdOcr,CustomApiUrl = config.CustomApiUrl};DataContext = OcrConfig;}private void BtnSave_Click(object sender, RoutedEventArgs e){DialogResult = true;Close();}private void BtnCancel_Click(object sender, RoutedEventArgs e){DialogResult = false;Close();}}
}
下面是主窗口的 XAML 代碼:
<Window x:Class="ImageOcrRenameTool.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="圖片OCR重命名工具 v1.0" Height="600" Width="800"WindowStartupLocation="CenterScreen"><Grid><!-- 頂部區域 --><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><!-- 標題欄 --><Border Grid.Row="0" Background="#333" Padding="10"><TextBlock Text="圖片OCR重命名工具" Foreground="White" FontSize="16" FontWeight="Bold"/></Border><!-- 主內容區域 --><Grid Grid.Row="1" Margin="10"><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><!-- 左側文件列表區域 --><GroupBox Grid.Column="0" Header="文件列表" Margin="0,0,5,0"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><Button x:Name="btnSelectFolder" Content="選擇文件夾" HorizontalAlignment="Left" Margin="5" Padding="5,3"Click="BtnSelectFolder_Click"/><ListBox x:Name="lstFiles" Grid.Row="1" Margin="5"SelectionChanged="LstFiles_SelectionChanged"ScrollViewer.HorizontalScrollBarVisibility="Auto"/></Grid></GroupBox><!-- 右側預覽和OCR結果區域 --><GroupBox Grid.Column="1" Header="預覽和OCR結果" Margin="5,0,0,0"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><TextBlock Grid.Row="0" Margin="5" Text="文件名:"/><TextBox x:Name="txtFileName" Grid.Row="0" Margin="60,5,5,5" IsReadOnly="True"/><ScrollViewer Grid.Row="1" Margin="5" HorizontalScrollBarVisibility="Auto"VerticalScrollBarVisibility="Auto"><Image x:Name="imgPreview" Stretch="Uniform" MouseWheel="ImgPreview_MouseWheel"/></ScrollViewer><TextBlock Grid.Row="2" Margin="5" Text="OCR識別結果:"/><TextBox x:Name="txtOcrResult" Grid.Row="3" Margin="5" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto"IsReadOnly="True"/></Grid></GroupBox></Grid><!-- 圖片導航區域 --><Grid Grid.Row="2" Margin="10" HorizontalAlignment="Center"><Button x:Name="btnPrev" Content="上一張" Padding="8,5" Margin="0,0,10,0" Click="BtnPrev_Click"/><Button x:Name="btnNext" Content="下一張" Padding="8,5" Margin="10,0,0,0" Click="BtnNext_Click"/><TextBlock x:Name="lblFileIndex" Margin="10,0" VerticalAlignment="Center" FontSize="12"/></Grid><!-- 底部操作區域 --><Grid Grid.Row="3" Margin="10"><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/><ColumnDefinition Width="Auto"/><ColumnDefinition Width="Auto"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><TextBlock x:Name="lblStatus" Grid.Column="0" Margin="5" VerticalAlignment="Center" FontSize="12"/><ProgressBar x:Name="progressBar" Grid.Column="1" Margin="5" Width="200" Height="20" VerticalAlignment="Center"/><TextBlock x:Name="lblProgress" Grid.Column="2" Margin="5" VerticalAlignment="Center" FontSize="12" Width="40"/><Button x:Name="btnProcess" Grid.Column="3" Content="開始處理" Padding="8,5" Margin="5" Click="BtnProcess_Click"/><Button x:Name="btnSettings" Grid.Column="4" Content="設置" Padding="8,5" Margin="5" Click="BtnSettings_Click"/></Grid></Grid>
</Window>
下面是設置窗口的 XAML 代碼:
<Window x:Class="ImageOcrRenameTool.SettingsWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="OCR設置" Height="300" Width="400"WindowStartupLocation="CenterScreen"ResizeMode="NoResize"><Grid Margin="10"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><TextBlock Grid.Row="0" Margin="5" Text="京東OCR設置"/><TextBlock Grid.Row="1" Margin="5" Text="AppKey:"/><TextBox Grid.Row="1" Margin="80,5,5,5" Text="{Binding AppKey}"/><TextBlock Grid.Row="2" Margin="5" Text="AppSecret:"/><TextBox Grid.Row="2" Margin="80,5,5,5" Text="{Binding AppSecret}" PasswordChar="*"/><CheckBox Grid.Row="3" Margin="5" Content="使用京東OCR服務" IsChecked="{Binding UseJdOcr}"/><TextBlock Grid.Row="4" Margin="5" Text="自定義API地址:"/><TextBox Grid.Row="4" Margin="80,5,5,5" Text="{Binding CustomApiUrl}"/><TextBlock Grid.Row="5" Margin="5" TextWrapping="Wrap" FontSize="10" Foreground="Gray"Text="注意: 使用京東OCR服務需要先在京東AI開放平臺注冊并獲取AppKey和AppSecret。"/><Grid Grid.Row="6" Margin="5"><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><Button Grid.Column="1" Content="保存" Padding="8,5" Margin="0,0,5,0" Click="BtnSave_Click"/><Button Grid.Column="2" Content="取消" Padding="8,5" Margin="5,0,0,0" Click="BtnCancel_Click"/></Grid></Grid>
</Window>
總結優化
-
功能總結:
- 實現了批量選擇圖片文件的功能
- 集成了JD OCR 服務進行文字識別
- 根據識別結果自動重命名圖片文件
- 提供了友好的用戶界面和操作體驗
- 支持圖片預覽和縮放功能
- 提供了JD?OCR 服務配置界面
-
性能優化:
- 使用異步處理避免 UI 線程阻塞
- 添加進度顯示,提升用戶體驗
- 對重命名沖突進行了處理,確保不會覆蓋已有文件
-
安全考慮:
- 將敏感的 OCR 配置信息保存到本地配置文件
- 添加了異常處理,確保程序在出現錯誤時不會崩潰
- 文件操作時進行了沖突檢測,避免數據丟失
-
可擴展性:
- 設計了可配置的 OCR 服務接口,便于集成其他 OCR 服務
- 可以進一步擴展功能,如添加文件過濾、批量操作日志等
-
使用建議:
- 在使用前需要在京東 AI 開放平臺注冊賬號并獲取 AppKey 和 AppSecret
- 對于大量圖片的處理,建議分批進行,避免內存占用過高
- 識別結果可能受到圖片質量、文字清晰度等因素影響
這個應用程序可以幫助用戶高效地對圖片進行重命名,提高工作效率。在實際使用中,你可以根據自己的需求進一步擴展和優化這個工具。