// Downloads the files models.config names into models/, checking each one's SHA-256. The models // are not in git; run this after cloning. Files already there with the right hash are skipped. // // jai fetch_models.jai every model // jai fetch_models.jai - mobile only the files ppocr-v5-mobile needs (about 21 MB) // // From RapidOCR's conversions of PaddleOCR's PP-OCRv5 (both Apache 2.0): // https://www.modelscope.cn/models/RapidAI/RapidOCR SOURCE :: "https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/master"; Model_File :: struct { name: string; source_path: string; sha256: string; server: bool; } FILES :: Model_File.[ .{"ch_PP-OCRv5_det_mobile.onnx", "onnx/PP-OCRv5/det/ch_PP-OCRv5_det_mobile.onnx", "4d97c44a20d30a81aad087d6a396b08f786c4635742afc391f6621f5c6ae78ae", false}, .{"ch_PP-OCRv5_rec_mobile.onnx", "onnx/PP-OCRv5/rec/ch_PP-OCRv5_rec_mobile.onnx", "5825fc7ebf84ae7a412be049820b4d86d77620f204a041697b0494669b1742c5", false}, .{"ppocrv5_dict.txt", "paddle/PP-OCRv5/rec/ch_PP-OCRv5_rec_mobile/ppocrv5_dict.txt", "d1979e9f794c464c0d2e0b70a7fe14dd978e9dc644c0e71f14158cdf8342af1b", false}, .{"ch_PP-OCRv5_det_server.onnx", "onnx/PP-OCRv5/det/ch_PP-OCRv5_det_server.onnx", "0f8846b1d4bba223a2a2f9d9b44022fbc22cc019051a602b41a7fda9667e4cad", true}, .{"ch_PP-OCRv5_rec_server.onnx", "onnx/PP-OCRv5/rec/ch_PP-OCRv5_rec_server.onnx", "e09385400eaaaef34ceff54aeb7c4f0f1fe014c27fa8b9905d4709b65746562a", true}, ]; #run,stallable { if !fetch() exit(1); } fetch :: () -> bool { set_build_options_dc(.{do_output = false}); mobile_only := array_find(get_build_options().compile_time_command_line, "mobile"); here := trim_right(path_strip_filename(#file), "/\\"); folder := tprint("%/models", here); make_directory_if_it_does_not_exist(folder); for FILES { if mobile_only && it.server continue; path := tprint("%/%", folder, it.name); if file_exists(path) && sha256_of(path) == it.sha256 { log("% is already here.", it.name); continue; } url := tprint("%/%", SOURCE, it.source_path); log("Downloading %...", url); result := run_command("curl", "-L", "--fail", "--progress-bar", "-o", path, url); if result.exit_code != 0 { log_error("curl failed for % (exit code %).", it.name, result.exit_code); return false; } hash := sha256_of(path); if hash != it.sha256 { log_error("% has SHA-256 %, expected %; it was deleted.", it.name, hash, it.sha256); file_delete(path); return false; } } log("The models are in %.", folder); return true; } // Lowercase hex, or "" when it could not be worked out. sha256_of :: (path: string) -> string { #if OS == .WINDOWS { result, output := run_command("certutil", "-hashfile", path, "SHA256", capture_and_return_output = true); if result.exit_code != 0 return ""; lines := split(output, "\n"); if lines.count < 2 return ""; return to_lower_copy(replace(trim(lines[1]), " ", "")); // line 2 is the hash } else { command := ifx OS == .MACOS then string.["shasum", "-a", "256", path] else string.["sha256sum", path]; result, output := run_command(..command, capture_and_return_output = true); if result.exit_code != 0 return ""; return to_lower_copy(trim(split(output, " ")[0])); } } #import "Basic"; #import "Compiler"; #import "File"; #import "File_Utilities"; #import "Process"; #import "String";