diff --git a/Project.toml b/Project.toml index 8811d26..b151df6 100644 --- a/Project.toml +++ b/Project.toml @@ -3,6 +3,16 @@ uuid = "308a64e0-2da8-4ffc-993f-c99ea7c8dc9b" authors = ["Wenbo Li and contributors"] version = "1.0.0-DEV" +[deps] +CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" +DICOM = "a26e6606-dd52-5f6a-a97f-4f611373d757" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0" +NIfTI = "a3a9e032-41b5-5fc4-967a-a6b7a19844d3" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + [compat] julia = "1" diff --git a/README.md b/README.md index 886a394..e69de29 100644 --- a/README.md +++ b/README.md @@ -1,3 +0,0 @@ -# imageToolBox - -[![Build Status](https://github.com/Wenbo Li/imageToolBox.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/Wenbo Li/imageToolBox.jl/actions/workflows/CI.yml?query=branch%3Amain) diff --git a/src/dicom_tools.jl b/src/dicom_tools.jl new file mode 100644 index 0000000..91aad77 --- /dev/null +++ b/src/dicom_tools.jl @@ -0,0 +1,116 @@ +using DICOM, Statistics + +""" + This function reads the dicom file. + - input: + 1. path to the dicom file + - output: + 1. dcm_data + 2. SID + 3. V_P + 4. image +""" +function read_dicom(path_to_file::String) + dcm_data = dcm_parse(path_to_file) + # image + image = dcm_data[(0x7fe0, 0x0010)] + # v_p + LorR = "" + try + LorR = uppercase(dcm_data[(0x0020,0x0062)]) + catch e + println("---------Error---------") + println(e) + println(curr_path) + println("---------Error---------") + end + V_P = "" + try + V_P = uppercase(dcm_data[(0x0018,0x5101)]) + catch e + println("---------Error---------") + println(e) + println(curr_path) + println("---------Error---------") + end + curr_key = LorR*" "*V_P + # SID + sid = [(0x0010, 0x0020)] + return dcm_data, sid, curr_key, image +end + +""" + This function gets the number of patches needed for a iamge. + - input: + 1. Matrix of Float32: the image + 2. Matrix of Float32: the roi + 3. Matrix of Float32: the binary mask representing the breast area. + - optional input: + 1. Float64: `thd`. The threshold of percentage of the white area. Default = 0.35 + 2. Int: 'patch_size'. The size of each patch. Default = 256 + - output: + 1. int: Number of without-BAC patches. + 2. int: Number of with-BAC patches. +""" +function get_num_of_patches(img::Matrix{Float32}, lbl::Matrix{Float32}, mask::Matrix{Float32}; thd = 0.35, patch_size = 256) + patch_size_half = round(Int, patch_size/2) + s = size(img) + x = ceil(Int, s[1]/patch_size) + floor(Int, (s[1]-patch_size_half)/patch_size) + y = ceil(Int, s[2]/patch_size) + floor(Int, (s[2]-patch_size_half)/patch_size) + ct_empty, ct_non_empty = 0, 0 + for i = 1 : x-1 + x_start = 1+(i-1)*patch_size_half + x_end = x_start+patch_size-1 + for j = 1 : y-1 + y_start = 1+(j-1)*patch_size_half + y_end = y_start+patch_size-1 + # check if this is forgounrd + if mean(mask[x_start:x_end, y_start:y_end]) > thd + # check if contains BAC + if sum(lbl[x_start:x_end, y_start:y_end]) > 0 + ct_non_empty += 1 + else + ct_empty += 1 + end + end + end + # right col + y_start, y_end = s[2]-patch_size+1, s[2] + # check if this is forgounrd + if mean(mask[x_start:x_end, y_start:y_end]) > thd + # check if contains BAC + if sum(lbl[x_start:x_end, y_start:y_end]) > 0 + ct_non_empty += 1 + else + ct_empty += 1 + end + end + end + # last row + x_start, x_end = s[1]-patch_size+1, s[1] + for j = 1 : y-1 + y_start = 1+(j-1)*patch_size_half + y_end = y_start+patch_size-1 + # check if this is forgounrd + if mean(mask[x_start:x_end, y_start:y_end]) > thd + # check if contains BAC + if sum(lbl[x_start:x_end, y_start:y_end]) > 0 + ct_non_empty += 1 + else + ct_empty += 1 + end + end + end + # right col + y_start, y_end = s[2]-patch_size+1, s[2] + # check if this is forgounrd + if mean(mask[x_start:x_end, y_start:y_end]) > thd + # check if contains BAC + if sum(lbl[x_start:x_end, y_start:y_end]) > 0 + ct_non_empty += 1 + else + ct_empty += 1 + end + end + return ct_empty, ct_non_empty +end \ No newline at end of file diff --git a/src/imageToolBox.jl b/src/imageToolBox.jl index 943abfb..a70c278 100644 --- a/src/imageToolBox.jl +++ b/src/imageToolBox.jl @@ -1,5 +1,17 @@ module imageToolBox -# Write your package code here. +using Pkg +Pkg.activate("..") +Pkg.instantiate() -end +include("dicom_tools.jl") +include("nifty_tools.jl") +include("png_tools.jl") +include("other_tools.jl") + +export save_to_csv, get_last_edit_time, search_files_by_ext, search_files_by_name, binary_search_SID +export zoom_pixel_values, copy_file, clean_directory, normalize_img +export read_png, get_breast_mask +export read_dicom, get_num_of_patches + +end \ No newline at end of file diff --git a/src/nifty_tools.jl b/src/nifty_tools.jl new file mode 100644 index 0000000..e69de29 diff --git a/src/other_tools.jl b/src/other_tools.jl new file mode 100644 index 0000000..5683c08 --- /dev/null +++ b/src/other_tools.jl @@ -0,0 +1,246 @@ +using CSV, DataFrames, Dates + + + + + +""" + This function saves data to local as csv file. + - input: + 1. String: file_name + 2. 1-D Array of Strings: column normalizes + 3. 2-D Array: data +""" +function save_to_csv(column_names_, data::Matrix, csv_name::String) + column_names = vec(column_names_) + # Ensure the column names match the data dimensions + if size(data, 2) != length(column_names) + throw(ArgumentError("Mismatch between number of columns in data and provided column names")) + end + + # Create a DataFrame from the data and column names + df = DataFrame(data, Symbol.(column_names)) + + # Save the DataFrame to a CSV file + CSV.write(csv_name, df) +end + +""" + This function gets the last edit time of a file. + - input: + 1. String: path to the file + - output: + 1. Dates: the last edit time +""" +function get_last_edit_time(file_path::String) + try + # Get the file's stat information + file_stat = stat(file_path) + + # Extract the last modification time (mtime) in seconds since the epoch + last_edit_time_epoch = file_stat.mtime + + # Convert the epoch time to a more readable DateTime format + last_edit_time = Dates.unix2datetime(last_edit_time_epoch) + + return last_edit_time + catch e + println("An error occurred: ", e) + return nothing + end +end + +""" + This function recursively look for all files in folders and subfolders. + - input: + 1. String: root dir + 2. String: extension of a file to search for. Note: input should be ".dcm" instead of "dcm". + - output: + 1. Array: a list of found paths. +""" +function search_files_by_ext(root_dir, file_ext) + + files=[] + + # collect curr folder info + sub_files, sub_dirs = [], [] + for f in readdir(root_dir) + curr_path = joinpath(root_dir, f) + + if isdir(curr_path) + push!(sub_dirs, curr_path) + else + push!(sub_files, curr_path) + end + end + dir_ct = size(sub_dirs)[1] + files_found = Array{Any}(undef, dir_ct) + + # folders: rec calls + Threads.@threads for i = 1 : dir_ct + files_found[i] = rec_search(sub_dirs[i], file_ext) + end + + # Append after multithreading to prevent data races + for i = 1 : dir_ct + append!(files, files_found[i]) + end + + # files: check if it's DICOM image + for f in sub_files + _, f_ext = splitext(f) + if f_ext == file_ext + push!(files, f) + end + end + + return files +end + +""" + This function recursively look for the target file. + - input: + 1. String: root dir + 2. String: file name of the target file + - output: + 1. Array of String: path to the target file. "" if not found. +""" +function search_files_by_name(root_dir, file_name) + # collect curr folder info + sub_files, sub_dirs = [], [] + for f in readdir(root_dir) + curr_path = joinpath(root_dir, f) + + if isdir(curr_path) + push!(sub_dirs, curr_path) + else + push!(sub_files, curr_path) + end + end + dir_ct = size(sub_dirs)[1] + files_found = Array{Any}(undef, dir_ct) + + # folders: rec calls + Threads.@threads for i = 1 : dir_ct + files_found[i] = search_files_by_name(sub_dirs[i], file_name) + end + + new_founds = Array{String, 1}(undef, 0) + # files: check if it's target_file + for f in sub_files + if rsplit(f, "/"; limit = 2)[2] == file_name + push!(new_founds, f) + end + end + + rslt = Array{String, 1}(undef, 0) + # combine paths + for f in files_found + for ff in f + if ff != "" + push!(rslt, ff) + end + end + end + for ff in new_founds + if ff != "" + push!(rslt, ff) + end + end + return unique(rslt) +end + +""" + This function looks for a target element in a array. The input array should be already sorted. + - input: + 1. Array of T: list contains the target + 2. T: the target + - output: + 1. int: index of the target in SID list. -1 if not found. +""" +function binary_search_SID(SIDs, target) + low = 1 + high = size(SIDs)[1] + + while low <= high + mid = div(low + high, 2) + + if SIDs[mid] == target + return mid + elseif SIDs[mid] < target + low = mid + 1 + else + high = mid - 1 + end + end + + return -1 # Return -1 if the target is not found +end + +""" + This function zoom pixel values of a image to range of [0, 1]. + - input: + 1. Matrix: the input image + - output: + 1. Matrix: the zoomed image. +""" +function zoom_pixel_values(img) + a, b = minimum(img), maximum(img) + img_ = (img .- a) ./ (b - a) + return img_ +end + +""" + This function copys a file to a new path. + - input: + 1. String: old path of the file + 2. String: new path of the file +""" +function copy_file(old_path::String, new_path::String) + # Check if the old DICOM file exists + if !isfile(old_path) + println("Error: The file does not exist.") + return + end + + # Check if the new path directory exists, if not create it + new_dir = dirname(new_path) + if !isdir(new_dir) + # println("Creating new directory: ", new_dir) + mkpath(new_dir) + end + + # Copy the file + if isfile(new_path) + return + end + try + cp(old_path, new_path) + catch e + println("An error occurred while copying the file: ", e) + end +end + +""" + This function deletes verything inside the dir. + - input: + 1. String: dir to be cleaned. +""" +function clean_directory(directory::String) + for item in readdir(directory) + item_path = joinpath(directory, item) + rm(item_path, recursive=true) + end +end + +""" + This function changes the image, making mean of all pixel values to 0, and std to 1 +""" +function normalize_img(img) + m = maximum(img) + img = m .- img + a = mean(img) + s = std(img) + img = (img .- a) ./ s + return img +end \ No newline at end of file diff --git a/src/png_tools.jl b/src/png_tools.jl new file mode 100644 index 0000000..610ded5 --- /dev/null +++ b/src/png_tools.jl @@ -0,0 +1,28 @@ +using Images + +""" + This function read a png file. + - input: + 1. String: Path to the png file + - output: + 1. Matrix{Float32}: the image +""" +function read_png(path_to_file::String) + _, f_ext = splitext(path_to_file) + if f_ext != ".png" + println("Error: not a png file.") + end + return Float32.(Images.load(path_to_file)) +end + +""" + This function find the breast area and create a mask. + - input: + 1. Matrix{Float32}: the raw image. + - output: + 1. Matrox{Float32}: the binary mask. +""" +function get_breast_mask(raw_img::Matrix{Float32}) + mask = 1 .- round.(imageToolBox.zoom_pixel_values(raw_img)) + return mask +end \ No newline at end of file diff --git a/test/runtest.jl b/test/runtest.jl new file mode 100644 index 0000000..caf606d --- /dev/null +++ b/test/runtest.jl @@ -0,0 +1,18 @@ +using JuliaNiftyReg +using Test + +@testset "other_tools.jl" begin + # Write your tests here. +end + +@testset "dicom_tools.jl" begin + # Write your tests here. +end + +@testset "nifty_tools.jl" begin + # Write your tests here. +end + +@testset "png_tools.jl" begin + # Write your tests here. +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl deleted file mode 100644 index f7b6b09..0000000 --- a/test/runtests.jl +++ /dev/null @@ -1,6 +0,0 @@ -using imageToolBox -using Test - -@testset "imageToolBox.jl" begin - # Write your tests here. -end diff --git a/testrun.ipynb b/testrun.ipynb new file mode 100644 index 0000000..50d95ec --- /dev/null +++ b/testrun.ipynb @@ -0,0 +1,718 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m project at `~/Desktop/imageToolBox.jl/imageToolBox.jl`\n", + "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m StatsAPI ───────────── v1.7.0\n", + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m PkgVersion ─────────── v0.3.3\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m ProgressMeter ──────── v1.9.0\n", + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m StaticArrayInterface ─ v1.4.1\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m JLD2 ───────────────── v0.4.34\n", + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m JLLWrappers ────────── v1.5.0\n", + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m StaticArrays ───────── v1.6.3\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m LogExpFunctions ────── v0.3.26\n", + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m CPUSummary ─────────── v0.2.4\n", + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m TiffImages ─────────── v0.6.5\n", + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m Rotations ──────────── v1.6.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Installed\u001b[22m\u001b[39m ColorSchemes ───────── v3.24.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/Desktop/imageToolBox.jl/imageToolBox.jl/Project.toml`\n", + " \u001b[90m[916415d5] \u001b[39m\u001b[92m+ Images v0.26.0\u001b[39m\n", + "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m `~/Desktop/imageToolBox.jl/imageToolBox.jl/Manifest.toml`\n", + " \u001b[90m[621f4979] \u001b[39m\u001b[92m+ AbstractFFTs v1.5.0\u001b[39m\n", + " \u001b[90m[79e6a3ab] \u001b[39m\u001b[92m+ Adapt v3.6.2\u001b[39m\n", + " \u001b[90m[ec485272] \u001b[39m\u001b[92m+ ArnoldiMethod v0.2.0\u001b[39m\n", + " \u001b[90m[4fba245c] \u001b[39m\u001b[92m+ ArrayInterface v7.4.11\u001b[39m\n", + " \u001b[90m[30b0a656] \u001b[39m\u001b[92m+ ArrayInterfaceCore v0.1.29\u001b[39m\n", + " \u001b[90m[13072b0f] \u001b[39m\u001b[92m+ AxisAlgorithms v1.0.1\u001b[39m\n", + " \u001b[90m[39de3d68] \u001b[39m\u001b[92m+ AxisArrays v0.4.7\u001b[39m\n", + " \u001b[90m[62783981] \u001b[39m\u001b[92m+ BitTwiddlingConvenienceFunctions v0.1.5\u001b[39m\n", + " \u001b[90m[fa961155] \u001b[39m\u001b[92m+ CEnum v0.4.2\u001b[39m\n", + " \u001b[90m[2a0fbf3d] \u001b[39m\u001b[92m+ CPUSummary v0.2.4\u001b[39m\n", + " \u001b[90m[aafaddc9] \u001b[39m\u001b[92m+ CatIndices v0.2.2\u001b[39m\n", + " \u001b[90m[d360d2e6] \u001b[39m\u001b[92m+ ChainRulesCore v1.16.0\u001b[39m\n", + " \u001b[90m[fb6a15b2] \u001b[39m\u001b[92m+ CloseOpenIntervals v0.1.12\u001b[39m\n", + " \u001b[90m[aaaa29a8] \u001b[39m\u001b[92m+ Clustering v0.15.4\u001b[39m\n", + " \u001b[90m[35d6a980] \u001b[39m\u001b[92m+ ColorSchemes v3.24.0\u001b[39m\n", + " \u001b[90m[3da002f7] \u001b[39m\u001b[92m+ ColorTypes v0.11.4\u001b[39m\n", + " \u001b[90m[c3611d14] \u001b[39m\u001b[92m+ ColorVectorSpace v0.10.0\u001b[39m\n", + " \u001b[90m[5ae59095] \u001b[39m\u001b[92m+ Colors v0.12.10\u001b[39m\n", + " \u001b[90m[ed09eef8] \u001b[39m\u001b[92m+ ComputationalResources v0.3.2\u001b[39m\n", + " \u001b[90m[150eb455] \u001b[39m\u001b[92m+ CoordinateTransformations v0.6.3\u001b[39m\n", + " \u001b[90m[adafc99b] \u001b[39m\u001b[92m+ CpuId v0.3.1\u001b[39m\n", + " \u001b[90m[dc8bdbbb] \u001b[39m\u001b[92m+ CustomUnitRanges v1.0.2\u001b[39m\n", + " \u001b[90m[b4f34e82] \u001b[39m\u001b[92m+ Distances v0.10.9\u001b[39m\n", + " \u001b[90m[ffbed154] \u001b[39m\u001b[92m+ DocStringExtensions v0.9.3\u001b[39m\n", + " \u001b[90m[4f61f5a4] \u001b[39m\u001b[92m+ FFTViews v0.3.2\u001b[39m\n", + " \u001b[90m[7a1cc6ca] \u001b[39m\u001b[92m+ FFTW v1.7.1\u001b[39m\n", + " \u001b[90m[5789e2e9] \u001b[39m\u001b[92m+ FileIO v1.16.1\u001b[39m\n", + " \u001b[90m[53c48c17] \u001b[39m\u001b[92m+ FixedPointNumbers v0.8.4\u001b[39m\n", + " \u001b[90m[a2bd30eb] \u001b[39m\u001b[92m+ Graphics v1.1.2\u001b[39m\n", + " \u001b[90m[86223c79] \u001b[39m\u001b[92m+ Graphs v1.8.0\u001b[39m\n", + " \u001b[90m[2c695a8d] \u001b[39m\u001b[92m+ HistogramThresholding v0.3.1\u001b[39m\n", + " \u001b[90m[3e5b6fbb] \u001b[39m\u001b[92m+ HostCPUFeatures v0.1.16\u001b[39m\n", + " \u001b[90m[615f187c] \u001b[39m\u001b[92m+ IfElse v0.1.1\u001b[39m\n", + " \u001b[90m[2803e5a7] \u001b[39m\u001b[92m+ ImageAxes v0.6.11\u001b[39m\n", + " \u001b[90m[c817782e] \u001b[39m\u001b[92m+ ImageBase v0.1.7\u001b[39m\n", + " \u001b[90m[cbc4b850] \u001b[39m\u001b[92m+ ImageBinarization v0.3.0\u001b[39m\n", + " \u001b[90m[f332f351] \u001b[39m\u001b[92m+ ImageContrastAdjustment v0.3.12\u001b[39m\n", + " \u001b[90m[a09fc81d] \u001b[39m\u001b[92m+ ImageCore v0.10.1\u001b[39m\n", + " \u001b[90m[89d5987c] \u001b[39m\u001b[92m+ ImageCorners v0.1.3\u001b[39m\n", + " \u001b[90m[51556ac3] \u001b[39m\u001b[92m+ ImageDistances v0.2.17\u001b[39m\n", + " \u001b[90m[6a3955dd] \u001b[39m\u001b[92m+ ImageFiltering v0.7.8\u001b[39m\n", + " \u001b[90m[82e4d734] \u001b[39m\u001b[92m+ ImageIO v0.6.7\u001b[39m\n", + " \u001b[90m[6218d12a] \u001b[39m\u001b[92m+ ImageMagick v1.3.0\u001b[39m\n", + " \u001b[90m[bc367c6b] \u001b[39m\u001b[92m+ ImageMetadata v0.9.9\u001b[39m\n", + " \u001b[90m[787d08f9] \u001b[39m\u001b[92m+ ImageMorphology v0.4.5\u001b[39m\n", + " \u001b[90m[2996bd0c] \u001b[39m\u001b[92m+ ImageQualityIndexes v0.3.7\u001b[39m\n", + " \u001b[90m[80713f31] \u001b[39m\u001b[92m+ ImageSegmentation v1.8.2\u001b[39m\n", + " \u001b[90m[4e3cecfd] \u001b[39m\u001b[92m+ ImageShow v0.3.8\u001b[39m\n", + " \u001b[90m[02fcd773] \u001b[39m\u001b[92m+ ImageTransformations v0.10.0\u001b[39m\n", + " \u001b[90m[916415d5] \u001b[39m\u001b[92m+ Images v0.26.0\u001b[39m\n", + " \u001b[90m[9b13fd28] \u001b[39m\u001b[92m+ IndirectArrays v1.0.0\u001b[39m\n", + " \u001b[90m[d25df0c9] \u001b[39m\u001b[92m+ Inflate v0.1.3\u001b[39m\n", + " \u001b[90m[1d092043] \u001b[39m\u001b[92m+ IntegralArrays v0.1.5\u001b[39m\n", + " \u001b[90m[a98d9a8b] \u001b[39m\u001b[92m+ Interpolations v0.14.7\u001b[39m\n", + " \u001b[90m[8197267c] \u001b[39m\u001b[92m+ IntervalSets v0.7.7\u001b[39m\n", + " \u001b[90m[92d709cd] \u001b[39m\u001b[92m+ IrrationalConstants v0.2.2\u001b[39m\n", + " \u001b[90m[c8e1da08] \u001b[39m\u001b[92m+ IterTools v1.8.0\u001b[39m\n", + " \u001b[90m[033835bb] \u001b[39m\u001b[92m+ JLD2 v0.4.34\u001b[39m\n", + " \u001b[90m[692b3bcd] \u001b[39m\u001b[92m+ JLLWrappers v1.5.0\u001b[39m\n", + " \u001b[90m[b835a17e] \u001b[39m\u001b[92m+ JpegTurbo v0.1.3\u001b[39m\n", + " \u001b[90m[10f19ff3] \u001b[39m\u001b[92m+ LayoutPointers v0.1.14\u001b[39m\n", + " \u001b[90m[8cdb02fc] \u001b[39m\u001b[92m+ LazyModules v0.3.1\u001b[39m\n", + " \u001b[90m[2ab3a3ac] \u001b[39m\u001b[92m+ LogExpFunctions v0.3.26\u001b[39m\n", + " \u001b[90m[bdcacae8] \u001b[39m\u001b[92m+ LoopVectorization v0.12.165\u001b[39m\n", + " \u001b[90m[1914dd2f] \u001b[39m\u001b[92m+ MacroTools v0.5.11\u001b[39m\n", + " \u001b[90m[d125e4d3] \u001b[39m\u001b[92m+ ManualMemory v0.1.8\u001b[39m\n", + " \u001b[90m[626554b9] \u001b[39m\u001b[92m+ MetaGraphs v0.7.2\u001b[39m\n", + " \u001b[90m[e94cdb99] \u001b[39m\u001b[92m+ MosaicViews v0.3.4\u001b[39m\n", + " \u001b[90m[77ba4419] \u001b[39m\u001b[92m+ NaNMath v1.0.2\u001b[39m\n", + " \u001b[90m[b8a86587] \u001b[39m\u001b[92m+ NearestNeighbors v0.4.13\u001b[39m\n", + " \u001b[90m[f09324ee] \u001b[39m\u001b[92m+ Netpbm v1.1.1\u001b[39m\n", + " \u001b[90m[6fe1bfb0] \u001b[39m\u001b[92m+ OffsetArrays v1.12.10\u001b[39m\n", + " \u001b[90m[52e1d378] \u001b[39m\u001b[92m+ OpenEXR v0.3.2\u001b[39m\n", + " \u001b[90m[f57f5aa1] \u001b[39m\u001b[92m+ PNGFiles v0.4.0\u001b[39m\n", + " \u001b[90m[5432bcbf] \u001b[39m\u001b[92m+ PaddedViews v0.5.12\u001b[39m\n", + " \u001b[90m[d96e819e] \u001b[39m\u001b[92m+ Parameters v0.12.3\u001b[39m\n", + " \u001b[90m[eebad327] \u001b[39m\u001b[92m+ PkgVersion v0.3.3\u001b[39m\n", + " \u001b[90m[1d0040c9] \u001b[39m\u001b[92m+ PolyesterWeave v0.2.1\u001b[39m\n", + "\u001b[33m⌅\u001b[39m \u001b[90m[f27b6e38] \u001b[39m\u001b[92m+ Polynomials v3.2.13\u001b[39m\n", + " \u001b[90m[92933f4c] \u001b[39m\u001b[92m+ ProgressMeter v1.9.0\u001b[39m\n", + " \u001b[90m[4b34888f] \u001b[39m\u001b[92m+ QOI v1.0.0\u001b[39m\n", + " \u001b[90m[94ee1d12] \u001b[39m\u001b[92m+ Quaternions v0.7.4\u001b[39m\n", + " \u001b[90m[b3c3ace0] \u001b[39m\u001b[92m+ RangeArrays v0.3.2\u001b[39m\n", + " \u001b[90m[c84ed2f1] \u001b[39m\u001b[92m+ Ratios v0.4.5\u001b[39m\n", + " \u001b[90m[c1ae055f] \u001b[39m\u001b[92m+ RealDot v0.1.0\u001b[39m\n", + " \u001b[90m[3cdcf5f2] \u001b[39m\u001b[92m+ RecipesBase v1.3.4\u001b[39m\n", + " \u001b[90m[dee08c22] \u001b[39m\u001b[92m+ RegionTrees v0.3.2\u001b[39m\n", + " \u001b[90m[ae029012] \u001b[39m\u001b[92m+ Requires v1.3.0\u001b[39m\n", + " \u001b[90m[6038ab10] \u001b[39m\u001b[92m+ Rotations v1.6.0\u001b[39m\n", + " \u001b[90m[94e857df] \u001b[39m\u001b[92m+ SIMDTypes v0.1.0\u001b[39m\n", + " \u001b[90m[476501e8] \u001b[39m\u001b[92m+ SLEEFPirates v0.6.39\u001b[39m\n", + " \u001b[90m[699a6c99] \u001b[39m\u001b[92m+ SimpleTraits v0.9.4\u001b[39m\n", + " \u001b[90m[47aef6b3] \u001b[39m\u001b[92m+ SimpleWeightedGraphs v1.4.0\u001b[39m\n", + " \u001b[90m[45858cf5] \u001b[39m\u001b[92m+ Sixel v0.1.3\u001b[39m\n", + " \u001b[90m[66db9d55] \u001b[39m\u001b[92m+ SnoopPrecompile v1.0.3\u001b[39m\n", + " \u001b[90m[cae243ae] \u001b[39m\u001b[92m+ StackViews v0.1.1\u001b[39m\n", + " \u001b[90m[aedffcd0] \u001b[39m\u001b[92m+ Static v0.8.8\u001b[39m\n", + " \u001b[90m[0d7ed370] \u001b[39m\u001b[92m+ StaticArrayInterface v1.4.1\u001b[39m\n", + " \u001b[90m[90137ffa] \u001b[39m\u001b[92m+ StaticArrays v1.6.3\u001b[39m\n", + " \u001b[90m[1e83bf80] \u001b[39m\u001b[92m+ StaticArraysCore v1.4.2\u001b[39m\n", + " \u001b[90m[82ae8749] \u001b[39m\u001b[92m+ StatsAPI v1.7.0\u001b[39m\n", + " \u001b[90m[2913bbd2] \u001b[39m\u001b[92m+ StatsBase v0.34.0\u001b[39m\n", + " \u001b[90m[62fd8b95] \u001b[39m\u001b[92m+ TensorCore v0.1.1\u001b[39m\n", + " \u001b[90m[8290d209] \u001b[39m\u001b[92m+ ThreadingUtilities v0.5.2\u001b[39m\n", + " \u001b[90m[731e570b] \u001b[39m\u001b[92m+ TiffImages v0.6.5\u001b[39m\n", + " \u001b[90m[06e1c1a7] \u001b[39m\u001b[92m+ TiledIteration v0.5.0\u001b[39m\n", + " \u001b[90m[3a884ed6] \u001b[39m\u001b[92m+ UnPack v1.0.2\u001b[39m\n", + " \u001b[90m[3d5dd08c] \u001b[39m\u001b[92m+ VectorizationBase v0.21.64\u001b[39m\n", + " \u001b[90m[efce3f68] \u001b[39m\u001b[92m+ WoodburyMatrices v0.5.5\u001b[39m\n", + " \u001b[90m[f5851436] \u001b[39m\u001b[92m+ FFTW_jll v3.3.10+0\u001b[39m\n", + "\u001b[33m⌅\u001b[39m \u001b[90m[c73af94c] \u001b[39m\u001b[92m+ ImageMagick_jll v6.9.10-12+3\u001b[39m\n", + " \u001b[90m[905a6f67] \u001b[39m\u001b[92m+ Imath_jll v3.1.7+0\u001b[39m\n", + " \u001b[90m[1d5cc7b8] \u001b[39m\u001b[92m+ IntelOpenMP_jll v2023.2.0+0\u001b[39m\n", + " \u001b[90m[aacddb02] \u001b[39m\u001b[92m+ JpegTurbo_jll v2.1.91+0\u001b[39m\n", + " \u001b[90m[88015f11] \u001b[39m\u001b[92m+ LERC_jll v3.0.0+1\u001b[39m\n", + "\u001b[33m⌅\u001b[39m \u001b[90m[89763e89] \u001b[39m\u001b[92m+ Libtiff_jll v4.4.0+0\u001b[39m\n", + " \u001b[90m[856f044c] \u001b[39m\u001b[92m+ MKL_jll v2023.2.0+0\u001b[39m\n", + " \u001b[90m[18a262bb] \u001b[39m\u001b[92m+ OpenEXR_jll v3.1.4+0\u001b[39m\n", + " \u001b[90m[3161d3a3] \u001b[39m\u001b[92m+ Zstd_jll v1.5.5+0\u001b[39m\n", + " \u001b[90m[b53b4c65] \u001b[39m\u001b[92m+ libpng_jll v1.6.38+0\u001b[39m\n", + " \u001b[90m[075b6546] \u001b[39m\u001b[92m+ libsixel_jll v1.10.3+0\u001b[39m\n", + " \u001b[90m[8ba89e20] \u001b[39m\u001b[92m+ Distributed\u001b[39m\n", + " \u001b[90m[4af54fe1] \u001b[39m\u001b[92m+ LazyArtifacts\u001b[39m\n", + " \u001b[90m[1a1011a3] \u001b[39m\u001b[92m+ SharedArrays\u001b[39m\n", + " \u001b[90m[4607b0f0] \u001b[39m\u001b[92m+ SuiteSparse\u001b[39m\n", + " \u001b[90m[05823500] \u001b[39m\u001b[92m+ OpenLibm_jll v0.8.1+0\u001b[39m\n", + "\u001b[36m\u001b[1m Info\u001b[22m\u001b[39m Packages marked with \u001b[33m⌅\u001b[39m have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`\n", + "\u001b[32m\u001b[1mPrecompiling\u001b[22m\u001b[39m " + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "project...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mSnoopPrecompile\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mStatsAPI\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mJLLWrappers\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mPkgVersion\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mLogExpFunctions\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mSimpleTraits\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mProgressMeter\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mIntelOpenMP_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mImath_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mlibpng_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mLERC_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mJpegTurbo_jll\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mZstd_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mFFTW_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mDistances\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mArrayInterfaceCore\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mCPUSummary\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mlibsixel_jll\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mOpenEXR_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mDistances → DistancesSparseArraysExt\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mLibtiff_jll\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mRecipesBase\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mPolyesterWeave\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mLogExpFunctions → LogExpFunctionsChainRulesCoreExt\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mOpenEXR\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageMagick_jll\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mStatsBase\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mColorSchemes\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mStaticArrays\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mAdapt → AdaptStaticArraysExt\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mStaticArrays → StaticArraysStatisticsExt\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mCoordinateTransformations\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mArnoldiMethod\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mArrayInterface\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mArrayInterface → ArrayInterfaceStaticArraysCoreExt\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mRegionTrees\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mOffsetArrays\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mRotations\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mStackViews\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mCatIndices\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mPaddedViews\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mMosaicViews\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mStaticArrayInterface\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mNearestNeighbors\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mStaticArrayInterface → StaticArrayInterfaceOffsetArraysExt\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mCloseOpenIntervals\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mLayoutPointers\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mStaticArrayInterface → StaticArrayInterfaceStaticArraysExt\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mInterpolations\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mTiledIteration\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mClustering\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mGraphs\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mSimpleWeightedGraphs\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mPolynomials\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mJLD2\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mPolynomials → PolynomialsChainRulesCoreExt\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mVectorizationBase\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mMetaGraphs\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mSLEEFPirates\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mMKL_jll\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageCore\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mSixel\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mJpegTurbo\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageBase\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mHistogramThresholding\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mImageMagick\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageShow\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageAxes\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mPNGFiles\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageTransformations\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageBinarization\u001b[39m\n", + "\u001b[32m ✓ \u001b[39m\u001b[90mImageMetadata\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mFFTW\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mFFTViews\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mNetpbm\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageContrastAdjustment\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mTiffImages\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageIO\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mLoopVectorization\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageMorphology\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageDistances\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageFiltering\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageSegmentation\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageCorners\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39m\u001b[90mImageQualityIndexes\u001b[39m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39mImages\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m ✓ \u001b[39mimageToolBox\n", + " 87 dependencies successfully precompiled in 84 seconds. 88 already precompiled.\n", + " \u001b[33m2\u001b[39m dependencies had warnings during precompilation:\u001b[33m\n", + "┌ \u001b[39mMKL_jll [856f044c-d86e-5d09-b602-aeab76dc8ba7]\u001b[33m\n", + "│ \u001b[39m\u001b[32m\u001b[1m Downloading\u001b[22m\u001b[39m artifact: MKL\u001b[33m\n", + "└ \u001b[39m\u001b[33m\n", + "┌ \u001b[39mimageToolBox [308a64e0-2da8-4ffc-993f-c99ea7c8dc9b]\u001b[33m\n", + "│ \u001b[39m\u001b[32m\u001b[1m Activating\u001b[22m\u001b[39m new project at `~/Desktop/imageToolBox.jl`\u001b[33m\n", + "└ \u001b[39m\n" + ] + } + ], + "source": [ + "using Pkg\n", + "Pkg.activate(\".\")\n", + "Pkg.add(\"Images\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.9.2", + "language": "julia", + "name": "julia-1.9" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.9.2" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +}