From d9f77361743d791d72cc3d7b5001596ac9f30cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=B6=F0=9D=9E=A2=E2=A4=AC=E2=AB=92=E2=B5=96s=E1=90=BC?= =?UTF-8?q?=CB=B6?= <91800957+exdysa@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:49:40 -0500 Subject: [PATCH 1/3] +save output, rebuild residual processing --- negate/conversion.py | 295 ++++++++++++++++++++++++++++++++ negate/inference.py | 23 +++ negate/save.py | 66 +++++++ negate/to_onnx.py | 219 ++++++++++++++++++++++++ results/variance_0128262145.png | Bin 0 -> 36338 bytes results/variance_0129261712.png | Bin 0 -> 35844 bytes 6 files changed, 603 insertions(+) create mode 100644 negate/conversion.py create mode 100644 negate/inference.py create mode 100644 negate/save.py create mode 100644 negate/to_onnx.py create mode 100644 results/variance_0128262145.png create mode 100644 results/variance_0129261712.png diff --git a/negate/conversion.py b/negate/conversion.py new file mode 100644 index 0000000..4ebc2bd --- /dev/null +++ b/negate/conversion.py @@ -0,0 +1,295 @@ +# adapted from ML-Model-CI + +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Copyright (c) NTU_CAP 2021. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing +# permissions and limitations under the License. + +from collections import defaultdict +from enum import Enum +from typing import List, Union + +import numpy as np +import onnxconverter_common +from mongoengine.fields import IntField, ListField, StringField, EmbeddedDocument +from onnx import TensorProto + + +class ModelInputFormat(Enum): + FORMAT_NONE = 0 + FORMAT_NHWC = 1 + FORMAT_NCHW = 2 + + +class DataType(Enum): + """ + @@@@.. cpp:enum:: DataType@@@@ Data types supported for input and output + tensors.@@ + """ + + # @@ .. cpp:enumerator:: DataType::INVALID = 0 + TYPE_INVALID = 0 + # @@ .. cpp:enumerator:: DataType::BOOL = 1 + TYPE_BOOL = 1 + # @@ .. cpp:enumerator:: DataType::UINT8 = 2 + TYPE_UINT8 = 2 + # @@ .. cpp:enumerator:: DataType::UINT16 = 3 + TYPE_UINT16 = 3 + # @@ .. cpp:enumerator:: DataType::UINT32 = 4 + TYPE_UINT32 = 4 + # @@ .. cpp:enumerator:: DataType::UINT64 = 5 + TYPE_UINT64 = 5 + # @@ .. cpp:enumerator:: DataType::INT8 = 6 + TYPE_INT8 = 6 + # @@ .. cpp:enumerator:: DataType::INT16 = 7 + TYPE_INT16 = 7 + # @@ .. cpp:enumerator:: DataType::INT32 = 8 + TYPE_INT32 = 8 + # @@ .. cpp:enumerator:: DataType::INT64 = 9 + TYPE_INT64 = 9 + # @@ .. cpp:enumerator:: DataType::FP16 = 10 + TYPE_FP16 = 10 + # @@ .. cpp:enumerator:: DataType::FP32 = 11 + TYPE_FP32 = 11 + # @@ .. cpp:enumerator:: DataType::FP64 = 12 + TYPE_FP64 = 12 + # @@ .. cpp:enumerator:: DataType::STRING = 13 + TYPE_STRING = 13 + + +class IOShapeDO(EmbeddedDocument): + name = StringField() + shape = ListField(IntField(), required=True) + dtype = StringField(required=True) + format = IntField(required=True) + + +def type_to_data_type(tensor_type): + import torch + + mapper = defaultdict( + lambda: DataType.TYPE_INVALID, + { + bool: DataType.TYPE_BOOL, + int: DataType.TYPE_INT32, + float: DataType.TYPE_FP32, + str: DataType.TYPE_STRING, + torch.bool: DataType.TYPE_BOOL, + torch.uint8: DataType.TYPE_UINT8, + torch.int: DataType.TYPE_INT32, + torch.int8: DataType.TYPE_INT8, + torch.int16: DataType.TYPE_INT16, + torch.int32: DataType.TYPE_INT32, + torch.int64: DataType.TYPE_INT64, + torch.float: DataType.TYPE_FP32, + torch.float16: DataType.TYPE_FP16, + torch.float32: DataType.TYPE_FP32, + torch.float64: DataType.TYPE_FP64, + np.dtype(np.bool): DataType.TYPE_BOOL, + np.dtype(np.uint8): DataType.TYPE_UINT8, + np.dtype(np.uint16): DataType.TYPE_UINT16, + np.dtype(np.uint32): DataType.TYPE_UINT32, + np.dtype(np.uint64): DataType.TYPE_UINT64, + np.dtype(np.float16): DataType.TYPE_FP16, + np.dtype(np.float32): DataType.TYPE_FP32, + np.dtype(np.float64): DataType.TYPE_FP64, + np.dtype(np.str_): DataType.TYPE_STRING, + TensorProto.UNDEFINED: DataType.TYPE_INVALID, + TensorProto.FLOAT: DataType.TYPE_FP32, + TensorProto.UINT8: DataType.TYPE_UINT8, + TensorProto.INT8: DataType.TYPE_INT8, + TensorProto.UINT16: DataType.TYPE_UINT16, + TensorProto.INT16: DataType.TYPE_INT16, + TensorProto.INT32: DataType.TYPE_INT32, + TensorProto.INT64: DataType.TYPE_INT64, + TensorProto.STRING: DataType.TYPE_STRING, + TensorProto.BOOL: DataType.TYPE_BOOL, + TensorProto.FLOAT16: DataType.TYPE_FP16, + TensorProto.DOUBLE: DataType.TYPE_FP64, + TensorProto.UINT32: DataType.TYPE_UINT32, + TensorProto.UINT64: DataType.TYPE_UINT64, + }, + ) + + return mapper[tensor_type] + + +def model_data_type_to_onnx(model_dtype): + mapper = { + DataType.TYPE_INVALID: onnxconverter_common, + DataType.TYPE_BOOL: onnxconverter_common.BooleanTensorType, + DataType.TYPE_INT32: onnxconverter_common.Int32TensorType, + DataType.TYPE_INT64: onnxconverter_common.Int64TensorType, + DataType.TYPE_FP32: onnxconverter_common.FloatTensorType, + DataType.TYPE_FP64: onnxconverter_common.DoubleTensorType, + DataType.TYPE_STRING: onnxconverter_common.StringType, + } + if isinstance(model_dtype, int): + model_dtype = DataType(model_dtype) + elif isinstance(model_dtype, str): + model_dtype = DataType[model_dtype] + elif not isinstance(model_dtype, DataType): + raise TypeError(f"model_dtype is expecting one of the type: `int`, `str`, or `DataType` but got {type(model_dtype)}") + return mapper[model_dtype] + + +def model_data_type_to_np(model_dtype): + mapper = { + DataType.TYPE_INVALID: None, + DataType.TYPE_BOOL: np.bool, + DataType.TYPE_UINT8: np.uint8, + DataType.TYPE_UINT16: np.uint16, + DataType.TYPE_UINT32: np.uint32, + DataType.TYPE_UINT64: np.uint64, + DataType.TYPE_INT8: np.int8, + DataType.TYPE_INT16: np.int16, + DataType.TYPE_INT32: np.int32, + DataType.TYPE_INT64: np.int64, + DataType.TYPE_FP16: np.float16, + DataType.TYPE_FP32: np.float32, + DataType.TYPE_FP64: np.float64, + DataType.TYPE_STRING: np.dtype(object), + } + + if isinstance(model_dtype, int): + model_dtype = DataType(model_dtype) + elif isinstance(model_dtype, str): + model_dtype = DataType[model_dtype] + elif not isinstance(model_dtype, DataType): + raise TypeError(f"model_dtype is expecting one of the type: `int`, `str`, or `DataType` but got {type(model_dtype)}") + return mapper[model_dtype] + + +def model_data_type_to_torch(model_dtype): + import torch + + mapper = { + DataType.TYPE_INVALID: None, + DataType.TYPE_BOOL: torch.bool, + DataType.TYPE_UINT8: torch.uint8, + DataType.TYPE_INT8: torch.int8, + DataType.TYPE_INT16: torch.int16, + DataType.TYPE_INT32: torch.int32, + DataType.TYPE_INT64: torch.int64, + DataType.TYPE_FP16: torch.float16, + DataType.TYPE_FP32: torch.float32, + DataType.TYPE_FP64: torch.float64, + } + + if isinstance(model_dtype, int): + model_dtype = DataType(model_dtype) + elif isinstance(model_dtype, str): + model_dtype = DataType[model_dtype] + elif not isinstance(model_dtype, DataType): + raise TypeError(f"model_dtype is expecting one of the type: `int`, `str`, or `DataType` but got {type(model_dtype)}") + return mapper[model_dtype] + + +class IOShape: + """Class for recording input and output shape with their data type. + + Args: + shape (List[int]): the shape of the input or output tensor. + dtype (DataType, type, str): The data type of the input or output tensor. + name (str): Tensor name. Default to None. + format (ModelInputFormat): Input format, used for TensorRT currently. + Default to `ModelInputFormat.FORMAT_NONE`. + """ + + def __init__(self, shape: List[int], dtype: Union[type, int, str, DataType], name: str | None = None, format: ModelInputFormat = ModelInputFormat.FORMAT_NONE): + """Initializer of input/output shape.""" + + import numpy as np + import torch + + # input / output name + self.name = name + # input / output tensor shape + self.shape = shape + # input format + if isinstance(format, str): + # TODO: for the convenience of `model.hub.manager`, to be removed + format = ModelInputFormat[format.upper()] + self.format = format + if isinstance(dtype, str): + try: + # if the type name is unified python type + dtype = type_to_data_type(eval(dtype)) + except NameError: + # try if the dtype is `DataType` + dtype = DataType[dtype.upper()] + elif isinstance(dtype, (type, int)): + dtype = type_to_data_type(dtype) + elif isinstance(dtype, (torch.dtype, np.dtype)): + dtype = type_to_data_type(dtype) + elif isinstance(dtype, DataType): + pass + else: + raise ValueError(f"data type should be an instance of `type`, type name or `DataType`, but got {type(dtype)}") + + # warning if the dtype is DataType.TYPE_INVALID + if dtype == DataType.TYPE_INVALID: + print("W: `dtype` is converted to invalid.") + + # input / output datatype + self.dtype = dtype + + @property + def batch_size(self) -> int: + return self.shape[0] + + @property + def example_shape(self): + return self.shape[1:] + + @property + def height(self): + if self.format == ModelInputFormat.FORMAT_NONE: + raise ValueError("No height for shape format of `ModelInputFormat.FORMAT_NONE`.") + if self.format == ModelInputFormat.FORMAT_NCHW: + return self.shape[2] + if self.format == ModelInputFormat.FORMAT_NHWC: + return self.shape[1] + + @property + def width(self): + if self.format == ModelInputFormat.FORMAT_NONE: + raise ValueError("No width for shape format of `ModelInputFormat.FORMAT_NONE`.") + if self.format == ModelInputFormat.FORMAT_NCHW: + return self.shape[3] + if self.format == ModelInputFormat.FORMAT_NHWC: + return self.shape[2] + + @property + def channel(self): + if self.format == ModelInputFormat.FORMAT_NONE: + raise ValueError("No channel for shape format of `ModelInputFormat.FORMAT_NONE`.") + if self.format == ModelInputFormat.FORMAT_NCHW: + return self.shape[1] + if self.format == ModelInputFormat.FORMAT_NHWC: + return self.shape[3] + + def to_io_shape_po(self): + """Convert IO shape business object to IO shape plain object.""" + return IOShapeDO(name=self.name, shape=self.shape, dtype=self.dtype.name, format=self.format) + + @staticmethod + def from_io_shape_po(io_shape_po: IOShapeDO): + """Create IO shape business object from IO shape plain object.""" + io_shape_bo = IOShape(name=io_shape_po.name, shape=io_shape_po.shape, dtype=io_shape_po.dtype, format=ModelInputFormat(io_shape_po.format)) + + return io_shape_bo + + def __str__(self): + return "{}, dtype={}, format={}".format(self.shape, self.dtype, self.format.name) diff --git a/negate/inference.py b/negate/inference.py new file mode 100644 index 0000000..ae1f80a --- /dev/null +++ b/negate/inference.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: MPL-2.0 AND LicenseRef-Commons-Clause-License-Condition-1.0 +# + +import numpy as np +import xgboost as xgb +import pickle + +# Load artifacts +model = xgb.Booster() +model.load_model("model.xgb") + +with open("pca.pkl", "rb") as f: + pca = pickle.load(f) + +meta = np.load("meta.npz") +scale_pos_weight = meta["scale_pos_weight"] + + +def predict(features: np.ndarray) -> np.ndarray: + # Apply the same PCA transformation + feats_pca = pca.transform(features) + dmat = xgb.DMatrix(feats_pca) + return model.predict(dmat) diff --git a/negate/save.py b/negate/save.py new file mode 100644 index 0000000..bb870d0 --- /dev/null +++ b/negate/save.py @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: MPL-2.0 AND LicenseRef-Commons-Clause-License-Condition-1.0 +# + +import numpy as np +import onnx +from onnxmltools.convert.xgboost.convert import convert as convert_xgboost +from skl2onnx import convert_sklearn +from skl2onnx.common.data_types import FloatTensorType as SkFloatTensorType +from onnxmltools.convert.common.data_types import FloatTensorType as OnnxFloatTensorType +from xgboost import Booster +from datetime import datetime +from negate import TrainResult +import os + + +def save_model(train_result: TrainResult, file_name: str = "negate_", extension: str = ".onnx") -> None: + model: Booster = train_result.model + feature_matrix = train_result.feature_matrix + seed = train_result.seed + pca = train_result.pca + time_data = datetime.now().strftime("%Y%m%d_%H%M%S") + time_meta_data = datetime.now().isoformat() + + initial_pca_types = [("input", SkFloatTensorType([None, feature_matrix.shape[1]]))] + negate_pca_onnx = convert_sklearn(pca, initial_types=initial_pca_types) + pca_file_name = os.path.join("models", f"{file_name}pca_{time_data}{extension}") + onnx.save(negate_pca_onnx, pca_file_name) + + negate_xgb = f"{file_name}_{time_data}.xgb" + model.save_model(negate_xgb) + + metadata_file = os.path.join("models", f"metadata_{time_data}.npz") + np.savez(metadata_file, seed=seed) + print(f"Models saved to disk. {pca_file_name} {negate_xgb} {metadata_file}") + + num_features: int = model.num_features + feature_names = model.feature_names + if feature_names: + # ONNX pattern (f0, f1, f2, ...) + if needs_fixing := any(not name.startswith("f") or not name[1:].isdigit() for name in feature_names if name): + print(f"Converting feature names : {needs_fixing}") + model.feature_names = [f"f{feature}" for feature in range(num_features)] + + # print(feature_matrix.shape[1]) + # print(num_features) + # initial_xgb_type = [("input", OnnxFloatTensorType([None, num_features]))] + # negate_xgb_onnx = f"{file_name}xgb{extension}" + + # try: + # onnx_model = convert_xgboost( + # model, + # initial_types=initial_xgb_type, + # target_opset=15, + # doc_string=f"XGBoost AI image detection - {str(seed)} - {str(time_meta_data)}", + # ) + # onnx.save(onnx_model, negate_xgb_onnx) + # except ValueError as e: + # if "Unsupported dimension type" in str(e): + # print("Error: Unsupported dimension ") + # if feature_matrix.shape[1] != num_features: + # print("Feature matrix dimension mismatch.") + # model.feature_names = [f"f{i}" for i in range(feature_matrix.shape[1])] + # else: + # print("Input shape matches expected format...") + # else: + # raise e diff --git a/negate/to_onnx.py b/negate/to_onnx.py new file mode 100644 index 0000000..b210780 --- /dev/null +++ b/negate/to_onnx.py @@ -0,0 +1,219 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# Copyright (c) NTU_CAP 2021. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +# or implied. See the License for the specific language governing +# permissions and limitations under the License. + +from pathlib import Path +from typing import Callable, Iterable, List, Optional + +import numpy as np +import onnx +import onnxmltools as onnxmltools +import torch +import torch.onnx +from onnxmltools import optimizer + +from negate.conversion import IOShape, model_data_type_to_torch, model_data_type_to_onnx + + +class ONNXConverter(object): + """Convert model to ONNX format.""" + + DEFAULT_OPSET = 10 + supported_framework = ["pytorch", "sklearn", "xgboost", "lightgbm"] + + class _Wrapper(object): + @staticmethod + def save(converter: Callable[..., "onnx.ModelProto"]): + def wrap(*args, save_path: Path | None = None, optimize: bool = True, override: bool = False, **kwargs) -> "onnx.ModelProto": + onnx_model = None + save_path_with_ext = None + + if save_path is not None: + save_path = Path(save_path) + save_path_with_ext = save_path.with_suffix(".onnx") + if save_path_with_ext.exists() and not override: + # file exist yet override flag is not set + print("Use cached model") + onnx_model = onnx.load(str(save_path_with_ext)) + + if onnx_model is None: + # otherwise, convert model + onnx_model = converter(*args, **kwargs) + + if optimize: + # optimize ONNX model + onnx_model = ONNXConverter.optim_onnx(onnx_model) + + if save_path_with_ext: + # save to disk + save_path.parent.mkdir(parents=True, exist_ok=True) + onnxmltools.utils.save_model(onnx_model, save_path_with_ext) + + return onnx_model + + return wrap + + @staticmethod + def from_pytorch( + model: torch.nn.Module, + save_path: Path, + inputs: Iterable[IOShape], + outputs: Iterable[IOShape], + model_input: Optional[List] = None, + opset: int = 10, + optimize: bool = True, + override: bool = False, + ): + """Save a loaded model in ONNX. + TODO: reuse inputs to pass model_input parameter later + + Arguments: + model (nn.Module): PyTorch model. + save_path (Path): ONNX saved path. + inputs (Iterable[IOShape]): Model input shapes. Batch size is indicated at the dimension. + outputs (Iterable[IOShape]): Model output shapes. + model_input (Optional[List]) : Sample Model input data + opset (int): ONNX op set version. + optimize (bool): Flag to optimize ONNX network. Default to `True`. + override (bool): Flag to override if the file with path to `save_path` has existed. Default to `False`. + """ + if save_path.with_suffix(".onnx").exists(): + if not override: # file exist yet override flag is not set + print("Use cached model") + return True + + export_kwargs = dict() + + # assert batch size + batch_sizes = list(map(lambda x: x.shape[0], inputs)) + if not all(batch_size == batch_sizes[0] for batch_size in batch_sizes): + raise ValueError("batch size for inputs (i.e. the first dimensions of `input.shape` are not consistent.") + batch_size = batch_sizes[0] + + if batch_size == -1: + export_kwargs["dynamic_axes"] = { + "input": {0: "batch_size"}, # variable length axes + "output": {0: "batch_size"}, + } + batch_size = 1 + else: + assert batch_size > 0 + + model.eval() + save_path.parent.mkdir(parents=True, exist_ok=True) + save_path_with_ext = save_path.with_suffix(".onnx") + + dummy_tensors, input_names, output_names = list(), list(), list() + for input_ in inputs: + dtype = model_data_type_to_torch(input_.dtype) + dummy_tensors.append(torch.rand(batch_size, *input_.shape[1:], requires_grad=True, dtype=dtype)) + input_names.append(input_.name) + for output_ in outputs: + output_names.append(output_.name) + if model_input is None: + model_input = tuple(dummy_tensors) + try: + torch.onnx.export( + model, # model being run + model_input, # model input (or a tuple for multiple inputs) + save_path_with_ext, # where to save the model (can be a file or file-like object) + export_params=True, # store the trained parameter weights inside the model file + opset_version=opset, # the ONNX version to export the model to + do_constant_folding=True, # whether to execute constant folding for optimization + input_names=input_names, # the model's input names + output_names=output_names, # the model's output names + keep_initializers_as_inputs=True, + **export_kwargs, + ) + + if optimize: + onnx_model = onnx.load(str(save_path_with_ext)) + network = ONNXConverter.optim_onnx(onnx_model) + onnx.save(network, str(save_path_with_ext)) + + print("ONNX format converted successfully") + return True + except Exception as e: + # TODO catch different types of error + print("Unable to convert to ONNX format, reason:") + print(e) + return False + + @staticmethod + @_Wrapper.save + def from_sklearn( + model, + inputs: Iterable[IOShape], + opset: int = DEFAULT_OPSET, + ): + initial_type = ONNXConverter.convert_initial_type(inputs) + onnx_model = onnxmltools.convert_sklearn(model, initial_types=initial_type, target_opset=opset) + print("sklearn to onnx converted successfully") + return onnx_model + + @staticmethod + @_Wrapper.save + def from_xgboost(model, inputs: Iterable[IOShape], opset: int = DEFAULT_OPSET): + initial_type = ONNXConverter.convert_initial_type(inputs) + onnx_model = onnxmltools.convert_xgboost(model, initial_types=initial_type, target_opset=opset) + print("xgboost to onnx converted successfully") + return onnx_model + + @staticmethod + @_Wrapper.save + def from_lightgbm(model, inputs: Iterable[IOShape], opset: int = DEFAULT_OPSET): + initial_type = ONNXConverter.convert_initial_type(inputs) + onnx_model = onnxmltools.convert_lightgbm(model, initial_types=initial_type, target_opset=opset) + print("lightgbm to onnx converted successfully") + return onnx_model + + @staticmethod + def convert_initial_type(inputs: Iterable[IOShape]): + # assert batch size + batch_sizes = list(map(lambda x: x.shape[0], inputs)) + if not all(batch_size == batch_sizes[0] for batch_size in batch_sizes): + raise ValueError("batch size for inputs (i.e. the first dimensions of `input.shape` are not consistent.") + batch_size = batch_sizes[0] + + if batch_size == -1: + batch_size = None + else: + assert batch_size > 0 + + initial_type = list() + for input_ in inputs: + initial_type.append((input_.name, model_data_type_to_onnx(input_.dtype)([batch_size, *input_.shape[1:]]))) + return initial_type + + @staticmethod + def optim_onnx(model: onnx.ModelProto, verbose=False): + """Optimize ONNX network""" + print("Begin Simplify ONNX Model ...") + passes = [ + "eliminate_deadend", + "eliminate_identity", + "extract_constant_to_initializer", + "eliminate_unused_initializer", + "fuse_add_bias_into_conv", + "fuse_bn_into_conv", + "fuse_matmul_add_bias_into_gemm", + ] + model = optimizer.optimize(model, passes) + + if verbose: + for m in onnx.helper.printable_graph(model.graph).split("\n"): + print(m) + print(f"Finished optimizing ONNX model. Total nodes: {len(model.graph.node)}") + return model diff --git a/results/variance_0128262145.png b/results/variance_0128262145.png new file mode 100644 index 0000000000000000000000000000000000000000..50ee2dc56f37e94c0b4038326cbccd56308eaf28 GIT binary patch literal 36338 zcmbrm2T;>%*Do4CP&OjiKtTi*M5#7$m|Ka-wfk1E~ zHLvO;5G<1j1k=(UHux|3XLd`%|0#N0Gx0ERvGwr0=VpV@x##iF$;HFT{=N{-#?9T{ z1#?mA{5dIk384ob9uM6WrKO$!=P#sO-0Y;kps;Ol6!wRjrtSy?*FDC+Oj&A~_6S6n z1@fxKokvMDGJDmJo}I>ddUc9RM!5s)=ML^ z61?V}qL>eN_OLf4Bs12hX};Pi%3dC`6%!v_hHD0E{Cq-a&g3onaDhY5 z$Fr6-zRjs>wbOkC1vdo!?)cDWIam2O`DHye{HH#|UGRMuIe5W)Y4T?E+ylc(oO80@ z%D}=S#HLXU8S_~)P!jd=vh$Kp1{mgG=#|Fto*GAGk#;dAdJRP_^O zorfI9nq#y-Xvg&~_gNMc)NU(Y+0f8>&L!A;Dnj|4*W&nXWqC=rq?9-&-PGUTp66VC z`j4X9=!^08)VZLkqZe%_<1GDMAD6HFelFNu=8O_hesoK2=;0HNAFUB;+mp=WF11@m z#kQ?lMZ=|zeZ&dVoCT6gZ62lC$8NHm=rQ%j;$nsO^7PqbI&aQtu(PxOV~noEVMXr` z`_Yr7s+T$gd9U+GnNLb}z_qTaVdW@~-Ya^}<_=Z3+j(0e0)O?;w;g(Ov)$ViZtQGt z6u6E5INFWa8gwi$e7;XWmq-&$^?lbU7%=2as`l~pe7zbbN4XWsBYDa7&(-Cbfs3xF z9J_+51+A^6F1_jD+FFiJmcH}Xcsr!g#5;x`?;saTXHG8b$dN~-%=*`^*7|SVDY8a7 zlZid7wAKWfF8Xjl!D*d@T_29UlTjydNHwVP3gzriJN2jk@F*K6Upa{|xXqK9skwOHYj^X3v4R*vYyH z3(u36r6}}nO275foo#w8pZvo#iq}MPAk%u~Tn$y%Tq3gl)s^S_DtI_IYWkTGdurEJ z6{y$ZD7AB6Y$boe({=k&)=nb~=M%A1hwqUmkjzV5s;lZ8JQ5unbG37AlCMgAQCy$j zUU^Pbl#;~h-v6?uLLVAw!{+inZvV!mmMb^xd+`i!HdB(ShwF{H%0D6ELp%JkP7il# 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

f-3;KiYeYy$+$=3A>#QR@YvqN z4QHD>v;QY0$|GA&zTm7qI}oy@BF502boCN`eT9|-p>W{q9q zb>aIdsavTkK4p$Nb08aN@9WFd6ND_pCHWg6FGl9WWe6JFH-t-Lma6?SFZHrL+!1VnS$gjUemPp9W(N6*jP0z>D+xWXU`*1adnzEVS!6 zWkXMUn)ip5i=dM z1ao1KW9!dEn?4)Q4mTHolLry6?eS&w|kC0nDOx2%7KS4!E6{8Bv*2 z*Izw6dO!5g*#lLlu3TM1<2iBt#}2+fc_aLRk$Do|Xo>8Z@rx zoUnhM?yhp2Os5e(y048zGn5O&F7eO}SSqRUnWz4di0$s?PwIb2glaOft|?h82)3 z?;6y(8zJ-qy38acs#~M;Hj*adCv{eBY%e0S2S!xL(V2{83e!|TO=T;VJtB`aP5lJ{e1Kq#@(m~WYtCzB~qdf$CRZc|=PBBel zc6qu-n{GY<{-07$oQCX$9{y-i&FQmKhWQ94_3yr59ZGQ%&fealIy&mWAcjinURalh z(mW^cCcoL4fP5@NaAzcfWH3Yhd8m>Hul=`{jTzH>UljC%Wx&zhBdz2)duRU7Sp)au ziXLaky!~*lvg{o&_L=Z#BNm1Em(uS)bv?b`7W{#DrOUx8jE|{t@?Gj!n(89IaUbDm zwuCZR7di!1fw?iQ7A8oiA;c20%&3N{n}5H7g!Z-j2AjetqEhqDrxibThOuF|86@e`+z5JJ}2KI3Cg!XsY8#J5^iQsd9_F;wev&h zcv~{nq8KuNv&>)Mbn)QDtid%NjPnGcBzpKFG-gsgd6+TwncsnWN(O13fPqmwhFk#2 zmt#=r-I(M#8~D@$ika;xI`<(C;y2PScU1a8Q~2o%Z}AbaX89n^|D ztxqtI%dz&u)jkB&jO-V$QB(2k59xk*DZKSjHn->NX%MlIPHAi;38kkiAJ_ZLGgrbO z-n1MT%SQH+wm{+i;3&wY{5KGM16q*x&MeKPYW|s{VE`=iZg1k?1xD2R1*heND zHDOQaE*hemU}fObc1;3WF0MR@F)jwwtS3iHV4dvB(1HIY4)wTNyFks4v(EGw6Y)Ed zeJhLWA4H^BP8983g$|%QI zF($rFIL2xfC%*Q>cA+uH^{WO0w_m67xbqD#*-wy^3 zHZ^Xe7GJx)t_@cd0kN%^x8NZN>F*Lz-vO-TL2@R1FmXYXx|C!&e>+C%6pYX?=XLa( z_eCKBA6k7WER9CRt$?FfUVot6j9J#PM>QsNK6bx9h+BZ>ViK#z00_M_B}-nc3^<^` zZ-BX2VG`@Xr;&%tHmreXz2MZRh+Mj1BVy- zO_&uDMagDEQw@*#+D>pY#G zPbx2IJnka_m)7MJBkZ{N=_Q7!#n%9a;6ON*pI`$V2tSULp%CABo@1KLyPnZ$b-vQy9&&g`H*&_RLWxzYT+N3-= z&dAPj6iHi#u9+dng#GR+uwC}M1sN)Sh^4|jemZ0Pu@hFnAhQSY=e1`i@V1&oO$(~A zca*z#??T9n#HQr|?1(;&K>U<#O~Ae{-gSv5eQvN++h=74StR81$g)$j_7!d7`RXGC zB6F6}O#p3O+0TsNc9xv=Or*(5_!XRN5Q~SU9P!H& z=Q1a2(c^RG6Sdl6r>2tc+U?tg2(E*9yt7^acH-0O?!yVxSS+%Qx#BZ4#dxM!{)GT% zVosS?{tV-n(}gLIk}bL%0Cc zAv61bg=XckI!he>cfg-{U|;2F+eB(U$F%D;{1bexN4TcLsqBC(u03EvLS?&6z>v+i z6dZdcg;IgN3&$mT@q!o}wAadl@~`CC z)s4A9f_JHH@GtqVg-&1DLAw3B^^!8&_xqddl+E~I(0X_8>%W?y53?TTWRn~tNg7Pc zJ}3aI?W?dlG~hlK%03y6AGBS$rRQD=Yrj%N zf#b-NqnFrx0^*m8(&JaQMXCi3zTZ5dT=q~r@!^$!@ANx5{cx_8&+n(aLzl9~q4MK4 z(4X!;R>aLPxH0RP47B?@Z{(Ze;KynFAFw2;H% zj34zF1Wxjd-Uq|%w4sVFl>i8neMAxR8Pen%`8KnuJekj_t*=!BwteF!Igx(%O5CAYbPPpAT3d z)9^zC9wWalG!u(6dq4OAZav#;m}l0L>>-rBMNw#P-NHK*}UGW18 zP3RC4QN*PeBURo5CwfCHYJAsj2|~I}J{iv5X;E*m7k>VNAXMU>vg6b>qv@T%#f*3t-umvo z!T)OQz2m9w|NrqrY3Zsf6_tvLrco-gX)OaRM=JIIm?-scBaD}Pf-{i_(K-l^zlE#b*?*H4GVisZz!`Zbt3d7nL~8=Rtu@>Iz%KhJbtx$bSFxIJ=tg8lUyG;*E)_aA}34K%X)|% z8AN; zD6+?~aMI3Kt+!~*s4}u6?c-iIlv@5c?Mkw;iDhA||C@3RVi!Ki)?ki3{d(cYe*Z`V zf#su|pV%l>WFB$`;!5B30w5|$HQ0J8{>7%yOM8JE)8*|wSQECq>Y}t_R^a>ibNeim*QoNf<7_B#y^4FAwP4Uuaf+x51FIem7TWe!XO0e zOXA^4jGOjfJwJ1O#B}+?nMU-^9NqB7EF3uliXwhNW#7ya!oJYquW&CAsR@oPo?nwP z%UKWX5+fg8!AM;d1x;)hB!!l=&W*dzSMn$MOhRS4&+R->UEWqyRZ>!_-D|y8cT!+{ zuz%4XKZ2cr%*4T`JMs0(;zqk@@lx}Tk;;~_*g5|cWOLTuv*m+Sh0>R8G1ntiw8o?$ zwlM7f`j*7D{3s19Ws340V<~;+-E7Cp z;YttITbg1a-0G2Cdykjbs?bF3ZN0czZDQG{9qg3w^NTBkcAG^(TN|lqwTT_~kW37L z^d<^jX8o4CT?WDu->>V7E4kMpyQ|5 zliPagk~VxD1ty>tAfmSm=@Y`3rGKlH{enf&<^pR9F1W|nSH5x(;NGZH+s!gG>I%c) zwJkM1;sSfNS{c$|v~64B`298B9#g9TC?9v_G{GGuG8F#ZCczi4TV{Ue2DOXb?g#tm z^wgwD#v7p*)Kg=jvM#asC`4YA2Z$QP&Tl6Obij=Qkxv?Nr;cJLi%zEEbbFVStGmKn2fYzgri$n3I2>DF)`V3{%dhiH+qzFU>M6pAPmc1;vtGkW?KRn_1#?@^taLqPbu;k$Br>r)P{k#_im;U^Nw zV_LX~Y%@t!x$mpQDmKROGP3FSlG-Vr=gf7fqE{v{hK;oOsnR@a3!a4?vR=CocxlbU zEo#F^B*{E;F&g@fzn=4NE?!vz%nm7E`RsFn#~?h{3V`dK42I$)76A99_tGB6jPL=(tWi_xzrWOZG} z?%5SXO)RSi55v4TC<2DcHK9&FRyq#IXkt@(j;3OZJZ>@OULR(fu$Ab-(c<65Q# zO0+9nY?Rsep_mlf!V`2h0SJXiC4z9Cmb`KAs9YEDnGU<~QKRCA7wMIEzDFiWmJ@l2 zgd@#LH`C}5RdD0RzX-#z)@}Is$(R%Q9Ni9bXIi1v=YNmSOUfvggPwSsg;neg{nzxQ zK#>lTt-eNSDq4?;Q4IuRWUeis?70(f5^S$Q4R452nSt$BG6iQWROy!VCmGq_pC{?t z`3EB{?1Z;Z&wG3W=YbA{h!$3E54SmhOQ?C?cJMUf?y-QMC`Ju<+*|<{XX5sjR53&b7`tm@t+~qQn|Cd#LeL^WeecJUGo4)eVCl|^7Z9aSU|`3iwt;Gi z{a-uFA%TFa$M`UR&-3IS-u}ak*|Bbm$<2Ell>6DK77eGcII3mFNiZ2{BOR4#wg0f3P~-W2j8RQvWmPJTs6tM9{6n0zP62 zdg|>Z?-ybkAB~j{26qkxlOE>j~;INbv?lJo)Ou5{seNMOd)t*C%KW3>+HiIKDsbS zt0d*1^peoD?fUCuX;hE4Z2FFwpUpYsFBF413NWm-V%w?s6~2am4x-m708M^H9S2(Jx3;) z@o3{{LnF;+xJUXA&7SJ$dqGya<>d6CGPP#0h-Ng9{y=WNZ`Al5`MH#@>h&B$M4$!= z%~6jod^zrO1q$yRY$6H*b36Q3lqcr_p3_8Dp^xAS5dRAd|@%pY@L zI~g}VrAU2YheHhg4NWjObF9V3eze{sizATHG|i`+xn(~^VdH8kJ0(&&FeRoM7A}GU zpczPs;yCaGZ#LS|Xyc-<$g+$;gL!$|Nc0;&b7wS6Vyy%>B3dcHQOD60+2}QaqgXb( z!+)hi@xv`dK1=^Rfg2TvmO|vTD0GOpV=Yr#O`k=+_SL>IaTBd2p^FJ zBTupMR7;j=qr`l|h82+M1``6Um&PD-e~(0uMfH3mUX*Ih^(ob3rXk2_q_3Ry zR|SbV^nwWL`KrCZW#T!@hrT~cad7_0-{~p9xTz_zf^Qy$zYBupbzLR+>s~CZ1bv`0 z(${Q2YCG?}yK1~1?oOv}W6IwD97E0Z2}s=)HnZC?zT8zS9K)R>PSd#^D8n&$1X~MI z>w8jD&1&!Zs$6^CtJRTRhVM}@)~p$Y@{vAboFdd~TIQDH!n9m~Bm$-Rl@{ihm>Y

aqa?GsDUJ*QMr&%?HEpC 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 literal 0 HcmV?d00001 diff --git a/results/variance_0129261712.png b/results/variance_0129261712.png new file mode 100644 index 0000000000000000000000000000000000000000..274f339fb204c35dd98d90ad0b4be01a9a6f0dae GIT binary patch literal 35844 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 Date: Thu, 29 Jan 2026 21:49:47 -0500 Subject: [PATCH 2/3] part 2 --- .gitignore | 3 +- README.md | 2 +- _version.py | 6 +- negate/__init__.py | 1 + negate/__main__.py | 16 +- negate/extract.py | 57 +++++--- negate/track.py | 4 +- negate/train.py | 8 +- pyproject.toml | 7 + results/Figure_0.png | Bin 36338 -> 0 bytes uv.lock | 341 ++++++++++++++++++++++++++++++++++++++++++- 11 files changed, 410 insertions(+), 35 deletions(-) delete mode 100644 results/Figure_0.png diff --git a/.gitignore b/.gitignore index e7922c3..f91c2e3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ assets/ .DS_Store *.lock .datasets/ -.continue/ \ No newline at end of file +.continue/ +models/ \ No newline at end of file diff --git a/README.md b/README.md index a008efe..692af69 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A command-line tool and Python library for processing and analyzing images, extr Demonstration of the provided test results and visualizations on our synthetic [darkshapes/a_slice dataset](https://huggingface.co/darkshapes/a_slice) and private works of human origin provided by consent from the generous artists at https://purelyhuman.xyz. -![Bar and grid graph comparing variance of the synthetic images](results/Figure_0.png) +![Bar and grid graph comparing variance of the synthetic images](results/variance_0129261712.png) ## Install diff --git a/_version.py b/_version.py index c9126f8..6485f2b 100644 --- a/_version.py +++ b/_version.py @@ -28,7 +28,7 @@ commit_id: COMMIT_ID __commit_id__: COMMIT_ID -__version__ = version = '0.1.dev26+g51645e1fc.d20260129' -__version_tuple__ = version_tuple = (0, 1, 'dev26', 'g51645e1fc.d20260129') +__version__ = version = '0.1.dev27+g7a88a7db8.d20260130' +__version_tuple__ = version_tuple = (0, 1, 'dev27', 'g7a88a7db8.d20260130') -__commit_id__ = commit_id = 'g51645e1fc' +__commit_id__ = commit_id = 'g7a88a7db8' diff --git a/negate/__init__.py b/negate/__init__.py index e27d867..6472bf2 100644 --- a/negate/__init__.py +++ b/negate/__init__.py @@ -5,3 +5,4 @@ from negate.extract import FeatureExtractor, DeviceName, features from negate.train import TrainResult, grade from negate.track import in_console +from negate.save import save_model diff --git a/negate/__main__.py b/negate/__main__.py index 283d264..1f2f411 100644 --- a/negate/__main__.py +++ b/negate/__main__.py @@ -2,17 +2,23 @@ # from datasets import Dataset -from negate import features, build_datasets, grade, TrainResult, in_console +from negate import features, build_datasets, grade, TrainResult, in_console, save_model +from sys import argv def main(): - dataset: Dataset = build_datasets() + match argv[-1]: + case "train": + dataset: Dataset = build_datasets() - features_dataset: Dataset = features(dataset) + features_dataset: Dataset = features(dataset) - train_result: TrainResult = grade(features_dataset) + train_result: TrainResult = grade(features_dataset) + save_model(train_result) - in_console(train_result) + in_console(train_result) + case _: + raise NotImplementedError if __name__ == "__main__": diff --git a/negate/extract.py b/negate/extract.py index 17bd734..b07df41 100644 --- a/negate/extract.py +++ b/negate/extract.py @@ -3,13 +3,14 @@ from enum import Enum +import numpy as np import torch import torchvision.transforms as T from datasets import Dataset from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL from huggingface_hub import snapshot_download - -from negate import build_datasets +from PIL.Image import Image, fromarray +from skimage.filters import laplace class DeviceName(str, Enum): @@ -20,6 +21,24 @@ class DeviceName(str, Enum): MPS = "mps" +class Residual: + def __init__(self, dtype: np.typing.DTypeLike = np.float32): + """Initialize the residual class""" + + self.dtype = dtype + self.dtype_name = repr(dtype).split(".")[-1] + + def __call__(self, image: Image) -> Image: + """Convert the input image to grayscale, apply a Laplace filter, + and replicate the result across three channels.""" + + greyscale = image.convert("L") + numeric_image = np.array(greyscale, dtype=self.dtype) + residual = laplace(numeric_image, ksize=3).astype(self.dtype) + residual_image: Image = fromarray(np.uint8(residual), mode="L").convert("RGB") + return residual_image + + class FeatureExtractor: transform: T.Compose = T.Compose( [ @@ -34,7 +53,7 @@ def __init__(self, model: str, device: DeviceName, dtype: torch.dtype) -> None: self.dtype = dtype self.model = model self.vae: AutoencoderKL | None = None - + self.residual_transform = Residual() if self.vae is None: self.create_vae() @@ -65,23 +84,27 @@ def cleanup(self) -> None: # type:ignore class BatchFeatures: def __call__(self, feature_extractor: FeatureExtractor, dataset: Dataset): """Extract VAE features from a batch of images.""" + assert hasattr(feature_extractor, "vae") - images = [] + features_list = [] for image in dataset["image"]: - image_tensor = feature_extractor.transform(image.convert("RGB")) - images.append(image_tensor) - - batch_tensor = torch.stack(images).to(feature_extractor.device, dtype=feature_extractor.dtype) - - # Extract features using VAE encoder (use tiled encoding if u want) - with torch.no_grad(): - latents = feature_extractor.vae.encode(batch_tensor).latent_dist.sample() - # Convert to float32 for numpy compatibility (bfloat16 not supported by numpy) - features = latents.cpu().float().numpy() - # The samples are like [16, 128, 128] which is very large tbh. in any case i treat them all as features and flatten them, - # but you can probably come up with a smarter way to do feature processing - features_list = [features[i].flatten() for i in range(len(features))] + color_image = image.convert("RGB") + gray_image = image.convert("L") + residual_image = feature_extractor.residual_transform(gray_image) + + # Convert to tensors + color_tensor = feature_extractor.transform(color_image) + residual_tensor = feature_extractor.transform(residual_image) + + batch_tensor = torch.stack([color_tensor, residual_tensor]).to(feature_extractor.device, dtype=feature_extractor.dtype) + + with torch.no_grad(): + latents_2_dim_h_w = feature_extractor.vae.encode(batch_tensor).latent_dist.sample() + mean_latent = latents_2_dim_h_w.mean(dim=0).cpu().float().numpy() + feature_vec = mean_latent.flatten() + + features_list.append(feature_vec) return {"features": features_list} diff --git a/negate/track.py b/negate/track.py index 28f0cb2..9c6c4ee 100644 --- a/negate/track.py +++ b/negate/track.py @@ -4,6 +4,7 @@ import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import accuracy_score, classification_report, f1_score, roc_auc_score +from datetime import datetime from negate import TrainResult @@ -47,7 +48,7 @@ def in_console(train_result: TrainResult) -> None: Real (0): {np.sum(labels == 0)} samples ({np.sum(labels == 0) / len(labels) * 100:.1f}%)\n Synthetic (1): {np.sum(labels == 1)} samples ({np.sum(labels == 1) / len(labels) * 100:.1f}%)\n Class imbalance ratio: {np.sum(labels == 0) / np.sum(labels == 1):.2f}:1\n - Random state seed: {seed}" + Random state seed: {seed} """) separator = lambda: print("=" * 60) @@ -79,4 +80,5 @@ def in_console(train_result: TrainResult) -> None: plt.ylabel("Explained Variance Ratio") plt.title("First 20 Components") plt.tight_layout() + plt.savefig(os.path.join("results", f"variance_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png") plt.show() diff --git a/negate/train.py b/negate/train.py index ed8db43..63fd51c 100644 --- a/negate/train.py +++ b/negate/train.py @@ -49,19 +49,19 @@ def grade(features_dataset: Dataset) -> TrainResult: "objective": "binary:logistic", "eval_metric": ["logloss", "aucpr"], "max_depth": 4, - "learning_rate": 0.1, - "subsample": 0.8, - "colsample_bytree": 0.8, + "learning_rate": 0.1, # keep as float + "subsample": 0.8, # keep as float + "colsample_bytree": 0.8, # keep as float "scale_pos_weight": scale_pos_weight, "seed": seed, } - evaluation_parameters = [(d_matrix_train, "train"), (d_matrix_test, "test")] evaluation_result = {} model = xgb.train( training_parameters, d_matrix_train, num_boost_round=200, evals=evaluation_parameters, early_stopping_rounds=10, evals_result=evaluation_result, verbose_eval=20 ) + return TrainResult( X_train=X_train, pca=pca, diff --git a/pyproject.toml b/pyproject.toml index 0542d2c..3d3d2ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,12 +21,19 @@ dependencies = [ "diffusers>=0.36.0", "huggingface-hub>=1.3.2", "matplotlib>=3.10.8", + "mongoengine>=0.29.1", "numpy==2.4.1", + "onnx>=1.20.1", + "onnxconverter-common>=1.16.0", + "onnxmltools>=1.15.0", + "onnxruntime>=1.24.0.dev20251031003", "opencv-python>=4.13.0.90", "pandas>=3.0.0", "pillow>=12.1.0", + "scikit-image>=0.26.0", "scikit-learn>=1.8.0", "seaborn>=0.13.2", + "skl2onnx>=1.19.1", "torch>=2.10.0", "torchvision>=0.25.0", "transformers>=4.57.3", diff --git a/results/Figure_0.png b/results/Figure_0.png deleted file mode 100644 index 50ee2dc56f37e94c0b4038326cbccd56308eaf28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36338 zcmbrm2T;>%*Do4CP&OjiKtTi*M5#7$m|Ka-wfk1E~ zHLvO;5G<1j1k=(UHux|3XLd`%|0#N0Gx0ERvGwr0=VpV@x##iF$;HFT{=N{-#?9T{ z1#?mA{5dIk384ob9uM6WrKO$!=P#sO-0Y;kps;Ol6!wRjrtSy?*FDC+Oj&A~_6S6n z1@fxKokvMDGJDmJo}I>ddUc9RM!5s)=ML^ z61?V}qL>eN_OLf4Bs12hX};Pi%3dC`6%!v_hHD0E{Cq-a&g3onaDhY5 z$Fr6-zRjs>wbOkC1vdo!?)cDWIam2O`DHye{HH#|UGRMuIe5W)Y4T?E+ylc(oO80@ z%D}=S#HLXU8S_~)P!jd=vh$Kp1{mgG=#|Fto*GAGk#;dAdJRP_^O zorfI9nq#y-Xvg&~_gNMc)NU(Y+0f8>&L!A;Dnj|4*W&nXWqC=rq?9-&-PGUTp66VC z`j4X9=!^08)VZLkqZe%_<1GDMAD6HFelFNu=8O_hesoK2=;0HNAFUB;+mp=WF11@m z#kQ?lMZ=|zeZ&dVoCT6gZ62lC$8NHm=rQ%j;$nsO^7PqbI&aQtu(PxOV~noEVMXr` z`_Yr7s+T$gd9U+GnNLb}z_qTaVdW@~-Ya^}<_=Z3+j(0e0)O?;w;g(Ov)$ViZtQGt z6u6E5INFWa8gwi$e7;XWmq-&$^?lbU7%=2as`l~pe7zbbN4XWsBYDa7&(-Cbfs3xF z9J_+51+A^6F1_jD+FFiJmcH}Xcsr!g#5;x`?;saTXHG8b$dN~-%=*`^*7|SVDY8a7 zlZid7wAKWfF8Xjl!D*d@T_29UlTjydNHwVP3gzriJN2jk@F*K6Upa{|xXqK9skwOHYj^X3v4R*vYyH z3(u36r6}}nO275foo#w8pZvo#iq}MPAk%u~Tn$y%Tq3gl)s^S_DtI_IYWkTGdurEJ z6{y$ZD7AB6Y$boe({=k&)=nb~=M%A1hwqUmkjzV5s;lZ8JQ5unbG37AlCMgAQCy$j zUU^Pbl#;~h-v6?uLLVAw!{+inZvV!mmMb^xd+`i!HdB(ShwF{H%0D6ELp%JkP7il# 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

f-3;KiYeYy$+$=3A>#QR@YvqN z4QHD>v;QY0$|GA&zTm7qI}oy@BF502boCN`eT9|-p>W{q9q zb>aIdsavTkK4p$Nb08aN@9WFd6ND_pCHWg6FGl9WWe6JFH-t-Lma6?SFZHrL+!1VnS$gjUemPp9W(N6*jP0z>D+xWXU`*1adnzEVS!6 zWkXMUn)ip5i=dM z1ao1KW9!dEn?4)Q4mTHolLry6?eS&w|kC0nDOx2%7KS4!E6{8Bv*2 z*Izw6dO!5g*#lLlu3TM1<2iBt#}2+fc_aLRk$Do|Xo>8Z@rx zoUnhM?yhp2Os5e(y048zGn5O&F7eO}SSqRUnWz4di0$s?PwIb2glaOft|?h82)3 z?;6y(8zJ-qy38acs#~M;Hj*adCv{eBY%e0S2S!xL(V2{83e!|TO=T;VJtB`aP5lJ{e1Kq#@(m~WYtCzB~qdf$CRZc|=PBBel zc6qu-n{GY<{-07$oQCX$9{y-i&FQmKhWQ94_3yr59ZGQ%&fealIy&mWAcjinURalh z(mW^cCcoL4fP5@NaAzcfWH3Yhd8m>Hul=`{jTzH>UljC%Wx&zhBdz2)duRU7Sp)au ziXLaky!~*lvg{o&_L=Z#BNm1Em(uS)bv?b`7W{#DrOUx8jE|{t@?Gj!n(89IaUbDm zwuCZR7di!1fw?iQ7A8oiA;c20%&3N{n}5H7g!Z-j2AjetqEhqDrxibThOuF|86@e`+z5JJ}2KI3Cg!XsY8#J5^iQsd9_F;wev&h zcv~{nq8KuNv&>)Mbn)QDtid%NjPnGcBzpKFG-gsgd6+TwncsnWN(O13fPqmwhFk#2 zmt#=r-I(M#8~D@$ika;xI`<(C;y2PScU1a8Q~2o%Z}AbaX89n^|D ztxqtI%dz&u)jkB&jO-V$QB(2k59xk*DZKSjHn->NX%MlIPHAi;38kkiAJ_ZLGgrbO z-n1MT%SQH+wm{+i;3&wY{5KGM16q*x&MeKPYW|s{VE`=iZg1k?1xD2R1*heND zHDOQaE*hemU}fObc1;3WF0MR@F)jwwtS3iHV4dvB(1HIY4)wTNyFks4v(EGw6Y)Ed zeJhLWA4H^BP8983g$|%QI zF($rFIL2xfC%*Q>cA+uH^{WO0w_m67xbqD#*-wy^3 zHZ^Xe7GJx)t_@cd0kN%^x8NZN>F*Lz-vO-TL2@R1FmXYXx|C!&e>+C%6pYX?=XLa( z_eCKBA6k7WER9CRt$?FfUVot6j9J#PM>QsNK6bx9h+BZ>ViK#z00_M_B}-nc3^<^` zZ-BX2VG`@Xr;&%tHmreXz2MZRh+Mj1BVy- zO_&uDMagDEQw@*#+D>pY#G zPbx2IJnka_m)7MJBkZ{N=_Q7!#n%9a;6ON*pI`$V2tSULp%CABo@1KLyPnZ$b-vQy9&&g`H*&_RLWxzYT+N3-= z&dAPj6iHi#u9+dng#GR+uwC}M1sN)Sh^4|jemZ0Pu@hFnAhQSY=e1`i@V1&oO$(~A zca*z#??T9n#HQr|?1(;&K>U<#O~Ae{-gSv5eQvN++h=74StR81$g)$j_7!d7`RXGC zB6F6}O#p3O+0TsNc9xv=Or*(5_!XRN5Q~SU9P!H& z=Q1a2(c^RG6Sdl6r>2tc+U?tg2(E*9yt7^acH-0O?!yVxSS+%Qx#BZ4#dxM!{)GT% zVosS?{tV-n(}gLIk}bL%0Cc zAv61bg=XckI!he>cfg-{U|;2F+eB(U$F%D;{1bexN4TcLsqBC(u03EvLS?&6z>v+i z6dZdcg;IgN3&$mT@q!o}wAadl@~`CC z)s4A9f_JHH@GtqVg-&1DLAw3B^^!8&_xqddl+E~I(0X_8>%W?y53?TTWRn~tNg7Pc zJ}3aI?W?dlG~hlK%03y6AGBS$rRQD=Yrj%N zf#b-NqnFrx0^*m8(&JaQMXCi3zTZ5dT=q~r@!^$!@ANx5{cx_8&+n(aLzl9~q4MK4 z(4X!;R>aLPxH0RP47B?@Z{(Ze;KynFAFw2;H% zj34zF1Wxjd-Uq|%w4sVFl>i8neMAxR8Pen%`8KnuJekj_t*=!BwteF!Igx(%O5CAYbPPpAT3d z)9^zC9wWalG!u(6dq4OAZav#;m}l0L>>-rBMNw#P-NHK*}UGW18 zP3RC4QN*PeBURo5CwfCHYJAsj2|~I}J{iv5X;E*m7k>VNAXMU>vg6b>qv@T%#f*3t-umvo z!T)OQz2m9w|NrqrY3Zsf6_tvLrco-gX)OaRM=JIIm?-scBaD}Pf-{i_(K-l^zlE#b*?*H4GVisZz!`Zbt3d7nL~8=Rtu@>Iz%KhJbtx$bSFxIJ=tg8lUyG;*E)_aA}34K%X)|% z8AN; zD6+?~aMI3Kt+!~*s4}u6?c-iIlv@5c?Mkw;iDhA||C@3RVi!Ki)?ki3{d(cYe*Z`V zf#su|pV%l>WFB$`;!5B30w5|$HQ0J8{>7%yOM8JE)8*|wSQECq>Y}t_R^a>ibNeim*QoNf<7_B#y^4FAwP4Uuaf+x51FIem7TWe!XO0e zOXA^4jGOjfJwJ1O#B}+?nMU-^9NqB7EF3uliXwhNW#7ya!oJYquW&CAsR@oPo?nwP z%UKWX5+fg8!AM;d1x;)hB!!l=&W*dzSMn$MOhRS4&+R->UEWqyRZ>!_-D|y8cT!+{ zuz%4XKZ2cr%*4T`JMs0(;zqk@@lx}Tk;;~_*g5|cWOLTuv*m+Sh0>R8G1ntiw8o?$ zwlM7f`j*7D{3s19Ws340V<~;+-E7Cp z;YttITbg1a-0G2Cdykjbs?bF3ZN0czZDQG{9qg3w^NTBkcAG^(TN|lqwTT_~kW37L z^d<^jX8o4CT?WDu->>V7E4kMpyQ|5 zliPagk~VxD1ty>tAfmSm=@Y`3rGKlH{enf&<^pR9F1W|nSH5x(;NGZH+s!gG>I%c) zwJkM1;sSfNS{c$|v~64B`298B9#g9TC?9v_G{GGuG8F#ZCczi4TV{Ue2DOXb?g#tm z^wgwD#v7p*)Kg=jvM#asC`4YA2Z$QP&Tl6Obij=Qkxv?Nr;cJLi%zEEbbFVStGmKn2fYzgri$n3I2>DF)`V3{%dhiH+qzFU>M6pAPmc1;vtGkW?KRn_1#?@^taLqPbu;k$Br>r)P{k#_im;U^Nw zV_LX~Y%@t!x$mpQDmKROGP3FSlG-Vr=gf7fqE{v{hK;oOsnR@a3!a4?vR=CocxlbU zEo#F^B*{E;F&g@fzn=4NE?!vz%nm7E`RsFn#~?h{3V`dK42I$)76A99_tGB6jPL=(tWi_xzrWOZG} z?%5SXO)RSi55v4TC<2DcHK9&FRyq#IXkt@(j;3OZJZ>@OULR(fu$Ab-(c<65Q# zO0+9nY?Rsep_mlf!V`2h0SJXiC4z9Cmb`KAs9YEDnGU<~QKRCA7wMIEzDFiWmJ@l2 zgd@#LH`C}5RdD0RzX-#z)@}Is$(R%Q9Ni9bXIi1v=YNmSOUfvggPwSsg;neg{nzxQ zK#>lTt-eNSDq4?;Q4IuRWUeis?70(f5^S$Q4R452nSt$BG6iQWROy!VCmGq_pC{?t z`3EB{?1Z;Z&wG3W=YbA{h!$3E54SmhOQ?C?cJMUf?y-QMC`Ju<+*|<{XX5sjR53&b7`tm@t+~qQn|Cd#LeL^WeecJUGo4)eVCl|^7Z9aSU|`3iwt;Gi z{a-uFA%TFa$M`UR&-3IS-u}ak*|Bbm$<2Ell>6DK77eGcII3mFNiZ2{BOR4#wg0f3P~-W2j8RQvWmPJTs6tM9{6n0zP62 zdg|>Z?-ybkAB~j{26qkxlOE>j~;INbv?lJo)Ou5{seNMOd)t*C%KW3>+HiIKDsbS zt0d*1^peoD?fUCuX;hE4Z2FFwpUpYsFBF413NWm-V%w?s6~2am4x-m708M^H9S2(Jx3;) z@o3{{LnF;+xJUXA&7SJ$dqGya<>d6CGPP#0h-Ng9{y=WNZ`Al5`MH#@>h&B$M4$!= z%~6jod^zrO1q$yRY$6H*b36Q3lqcr_p3_8Dp^xAS5dRAd|@%pY@L zI~g}VrAU2YheHhg4NWjObF9V3eze{sizATHG|i`+xn(~^VdH8kJ0(&&FeRoM7A}GU zpczPs;yCaGZ#LS|Xyc-<$g+$;gL!$|Nc0;&b7wS6Vyy%>B3dcHQOD60+2}QaqgXb( z!+)hi@xv`dK1=^Rfg2TvmO|vTD0GOpV=Yr#O`k=+_SL>IaTBd2p^FJ zBTupMR7;j=qr`l|h82+M1``6Um&PD-e~(0uMfH3mUX*Ih^(ob3rXk2_q_3Ry zR|SbV^nwWL`KrCZW#T!@hrT~cad7_0-{~p9xTz_zf^Qy$zYBupbzLR+>s~CZ1bv`0 z(${Q2YCG?}yK1~1?oOv}W6IwD97E0Z2}s=)HnZC?zT8zS9K)R>PSd#^D8n&$1X~MI z>w8jD&1&!Zs$6^CtJRTRhVM}@)~p$Y@{vAboFdd~TIQDH!n9m~Bm$-Rl@{ihm>Y

aqa?GsDUJ*QMr&%?HEpC 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 diff --git a/uv.lock b/uv.lock index 8f51530..65a07df 100644 --- a/uv.lock +++ b/uv.lock @@ -212,6 +212,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "coloredlogs" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "humanfriendly" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" }, +] + [[package]] name = "contourpy" version = "1.3.3" @@ -352,6 +364,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, ] +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + [[package]] name = "filelock" version = "3.20.3" @@ -361,6 +382,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, ] +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, +] + [[package]] name = "fonttools" version = "4.61.1" @@ -549,7 +578,7 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.3.4" +version = "1.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -563,9 +592,21 @@ dependencies = [ { name = "typer-slim" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/af/25/74af9d16cd59ae15b12467a79a84aa0fe24be4aba68fc4da0c1864d49c17/huggingface_hub-1.3.4.tar.gz", hash = "sha256:c20d5484a611b7b7891d272e8fc9f77d5de025b0480bdacfa858efb3780b455f", size = 627683, upload-time = "2026-01-26T14:05:10.656Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/e9/2658cb9bc4c72a67b7f87650e827266139befaf499095883d30dabc4d49f/huggingface_hub-1.3.5.tar.gz", hash = "sha256:8045aca8ddab35d937138f3c386c6d43a275f53437c5c64cdc9aa8408653b4ed", size = 627456, upload-time = "2026-01-29T10:34:19.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/07/3d0c34c345043c6a398a5882e196b2220dc5861adfa18322448b90908f26/huggingface_hub-1.3.4-py3-none-any.whl", hash = "sha256:a0c526e76eb316e96a91e8a1a7a93cf66b0dd210be1a17bd5fc5ae53cba76bfd", size = 536611, upload-time = "2026-01-26T14:05:08.549Z" }, + { url = "https://files.pythonhosted.org/packages/f9/84/a579b95c46fe8e319f89dc700c087596f665141575f4dcf136aaa97d856f/huggingface_hub-1.3.5-py3-none-any.whl", hash = "sha256:fe332d7f86a8af874768452295c22cd3f37730fb2463cf6cc3295e26036f8ef9", size = 536675, upload-time = "2026-01-29T10:34:17.713Z" }, +] + +[[package]] +name = "humanfriendly" +version = "10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyreadline3", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" }, ] [[package]] @@ -577,6 +618,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] +[[package]] +name = "imageio" +version = "2.37.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/6f/606be632e37bf8d05b253e8626c2291d74c691ddc7bcdf7d6aaf33b32f6a/imageio-2.37.2.tar.gz", hash = "sha256:0212ef2727ac9caa5ca4b2c75ae89454312f440a756fcfc8ef1993e718f50f8a", size = 389600, upload-time = "2025-11-04T14:29:39.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/fe/301e0936b79bcab4cacc7548bf2853fc28dced0a578bab1f7ef53c9aa75b/imageio-2.37.2-py3-none-any.whl", hash = "sha256:ad9adfb20335d718c03de457358ed69f141021a333c40a53e57273d8a5bd0b9b", size = 317646, upload-time = "2025-11-04T14:29:37.948Z" }, +] + [[package]] name = "importlib-metadata" version = "8.7.1" @@ -678,6 +732,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ab/84/83292c2af8912eab30d4931fbd09d41e980ff014f10479053ed15e8f46c2/kiwisolver-1.4.10rc0-cp314-cp314t-win_arm64.whl", hash = "sha256:0786b140f2810f7cc425aa643538a48f2bbec1348fd21821939255cb12d4ad84", size = 70310, upload-time = "2025-08-10T20:22:14.827Z" }, ] +[[package]] +name = "lazy-loader" +version = "0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6b/c875b30a1ba490860c93da4cabf479e03f584eba06fe5963f6f6644653d8/lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1", size = 15431, upload-time = "2024-04-05T13:03:12.261Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc", size = 12097, upload-time = "2024-04-05T13:03:10.514Z" }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -777,6 +843,49 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, ] +[[package]] +name = "ml-dtypes" +version = "0.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/4a/c27b42ed9b1c7d13d9ba8b6905dece787d6259152f2309338aed29b2447b/ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453", size = 692314, upload-time = "2025-11-17T22:32:31.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/a1/4008f14bbc616cfb1ac5b39ea485f9c63031c4634ab3f4cf72e7541f816a/ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48", size = 676888, upload-time = "2025-11-17T22:31:56.907Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b7/dff378afc2b0d5a7d6cd9d3209b60474d9819d1189d347521e1688a60a53/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b", size = 5036993, upload-time = "2025-11-17T22:31:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/eb/33/40cd74219417e78b97c47802037cf2d87b91973e18bb968a7da48a96ea44/ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d", size = 5010956, upload-time = "2025-11-17T22:31:59.931Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8b/200088c6859d8221454825959df35b5244fa9bdf263fd0249ac5fb75e281/ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328", size = 212224, upload-time = "2025-11-17T22:32:01.349Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/dfc3775cb36367816e678f69a7843f6f03bd4e2bcd79941e01ea960a068e/ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175", size = 160798, upload-time = "2025-11-17T22:32:02.864Z" }, + { url = "https://files.pythonhosted.org/packages/4f/74/e9ddb35fd1dd43b1106c20ced3f53c2e8e7fc7598c15638e9f80677f81d4/ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6", size = 702083, upload-time = "2025-11-17T22:32:04.08Z" }, + { url = "https://files.pythonhosted.org/packages/74/f5/667060b0aed1aa63166b22897fdf16dca9eb704e6b4bbf86848d5a181aa7/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d", size = 5354111, upload-time = "2025-11-17T22:32:05.546Z" }, + { url = "https://files.pythonhosted.org/packages/40/49/0f8c498a28c0efa5f5c95a9e374c83ec1385ca41d0e85e7cf40e5d519a21/ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298", size = 5366453, upload-time = "2025-11-17T22:32:07.115Z" }, + { url = "https://files.pythonhosted.org/packages/8c/27/12607423d0a9c6bbbcc780ad19f1f6baa2b68b18ce4bddcdc122c4c68dc9/ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6", size = 225612, upload-time = "2025-11-17T22:32:08.615Z" }, + { url = "https://files.pythonhosted.org/packages/e5/80/5a5929e92c72936d5b19872c5fb8fc09327c1da67b3b68c6a13139e77e20/ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1", size = 164145, upload-time = "2025-11-17T22:32:09.782Z" }, + { url = "https://files.pythonhosted.org/packages/72/4e/1339dc6e2557a344f5ba5590872e80346f76f6cb2ac3dd16e4666e88818c/ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22", size = 673781, upload-time = "2025-11-17T22:32:11.364Z" }, + { url = "https://files.pythonhosted.org/packages/04/f9/067b84365c7e83bda15bba2b06c6ca250ce27b20630b1128c435fb7a09aa/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465", size = 5036145, upload-time = "2025-11-17T22:32:12.783Z" }, + { url = "https://files.pythonhosted.org/packages/c6/bb/82c7dcf38070b46172a517e2334e665c5bf374a262f99a283ea454bece7c/ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f", size = 5010230, upload-time = "2025-11-17T22:32:14.38Z" }, + { url = "https://files.pythonhosted.org/packages/e9/93/2bfed22d2498c468f6bcd0d9f56b033eaa19f33320389314c19ef6766413/ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56", size = 221032, upload-time = "2025-11-17T22:32:15.763Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/9c912fe6ea747bb10fe2f8f54d027eb265db05dfb0c6335e3e063e74e6e8/ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049", size = 163353, upload-time = "2025-11-17T22:32:16.932Z" }, + { url = "https://files.pythonhosted.org/packages/cd/02/48aa7d84cc30ab4ee37624a2fd98c56c02326785750cd212bc0826c2f15b/ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9", size = 702085, upload-time = "2025-11-17T22:32:18.175Z" }, + { url = "https://files.pythonhosted.org/packages/5a/e7/85cb99fe80a7a5513253ec7faa88a65306be071163485e9a626fce1b6e84/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7", size = 5355358, upload-time = "2025-11-17T22:32:19.7Z" }, + { url = "https://files.pythonhosted.org/packages/79/2b/a826ba18d2179a56e144aef69e57fb2ab7c464ef0b2111940ee8a3a223a2/ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf", size = 5366332, upload-time = "2025-11-17T22:32:21.193Z" }, + { url = "https://files.pythonhosted.org/packages/84/44/f4d18446eacb20ea11e82f133ea8f86e2bf2891785b67d9da8d0ab0ef525/ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1", size = 236612, upload-time = "2025-11-17T22:32:22.579Z" }, + { url = "https://files.pythonhosted.org/packages/ad/3f/3d42e9a78fe5edf792a83c074b13b9b770092a4fbf3462872f4303135f09/ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d", size = 168825, upload-time = "2025-11-17T22:32:23.766Z" }, +] + +[[package]] +name = "mongoengine" +version = "0.29.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pymongo" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/0b/f0bd3da47c77b2d48103b42b9a38a70de9c41c979dd681a9a6aff43bf2eb/mongoengine-0.29.1.tar.gz", hash = "sha256:3b43abaf2d5f0b7d39efc2b7d9e78f4d4a5dc7ce92b9889ba81a5a9b8dee3cf3", size = 168735, upload-time = "2024-09-19T08:41:22.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/52/a0788a31f8ec2cfb508e1fb29c321d5082f0aa58bc88ba118c898e72f612/mongoengine-0.29.1-py3-none-any.whl", hash = "sha256:9302ec407dd60f47f62cc07684d9f6cac87f1e93283c54203851788104d33df4", size = 112377, upload-time = "2024-09-19T08:41:20.626Z" }, +] + [[package]] name = "mpmath" version = "1.3.0" @@ -893,12 +1002,19 @@ dependencies = [ { name = "diffusers" }, { name = "huggingface-hub" }, { name = "matplotlib" }, + { name = "mongoengine" }, { name = "numpy" }, + { name = "onnx" }, + { name = "onnxconverter-common" }, + { name = "onnxmltools" }, + { name = "onnxruntime" }, { name = "opencv-python" }, { name = "pandas" }, { name = "pillow" }, + { name = "scikit-image" }, { name = "scikit-learn" }, { name = "seaborn" }, + { name = "skl2onnx" }, { name = "torch" }, { name = "torchvision" }, { name = "transformers" }, @@ -920,12 +1036,19 @@ requires-dist = [ { name = "diffusers", specifier = ">=0.36.0" }, { name = "huggingface-hub", specifier = ">=1.3.2" }, { name = "matplotlib", specifier = ">=3.10.8" }, + { name = "mongoengine", specifier = ">=0.29.1" }, { name = "numpy", specifier = "==2.4.1" }, + { name = "onnx", specifier = ">=1.20.1" }, + { name = "onnxconverter-common", specifier = ">=1.16.0" }, + { name = "onnxmltools", specifier = ">=1.15.0" }, + { name = "onnxruntime", specifier = ">=1.24.0.dev20251031003" }, { name = "opencv-python", specifier = ">=4.13.0.90" }, { name = "pandas", specifier = ">=3.0.0" }, { name = "pillow", specifier = ">=12.1.0" }, + { name = "scikit-image", specifier = ">=0.26.0" }, { name = "scikit-learn", specifier = ">=1.8.0" }, { name = "seaborn", specifier = ">=0.13.2" }, + { name = "skl2onnx", specifier = ">=1.19.1" }, { name = "torch", specifier = ">=2.10.0" }, { name = "torchvision", specifier = ">=0.25.0" }, { name = "transformers", specifier = ">=4.57.3" }, @@ -1143,6 +1266,78 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" }, ] +[[package]] +name = "onnx" +version = "1.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ml-dtypes" }, + { name = "numpy" }, + { name = "protobuf" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/8a/335c03a8683a88a32f9a6bb98899ea6df241a41df64b37b9696772414794/onnx-1.20.1.tar.gz", hash = "sha256:ded16de1df563d51fbc1ad885f2a426f814039d8b5f4feb77febe09c0295ad67", size = 12048980, upload-time = "2026-01-10T01:40:03.043Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/4c/4b17e82f91ab9aa07ff595771e935ca73547b035030dc5f5a76e63fbfea9/onnx-1.20.1-cp312-abi3-macosx_12_0_universal2.whl", hash = "sha256:1d923bb4f0ce1b24c6859222a7e6b2f123e7bfe7623683662805f2e7b9e95af2", size = 17903547, upload-time = "2026-01-10T01:39:31.015Z" }, + { url = "https://files.pythonhosted.org/packages/64/5e/1bfa100a9cb3f2d3d5f2f05f52f7e60323b0e20bb0abace1ae64dbc88f25/onnx-1.20.1-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ddc0b7d8b5a94627dc86c533d5e415af94cbfd103019a582669dad1f56d30281", size = 17412021, upload-time = "2026-01-10T01:39:33.885Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/d3fec0dcf9a7a99e7368112d9c765154e81da70fcba1e3121131a45c245b/onnx-1.20.1-cp312-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9336b6b8e6efcf5c490a845f6afd7e041c89a56199aeda384ed7d58fb953b080", size = 17510450, upload-time = "2026-01-10T01:39:36.589Z" }, + { url = "https://files.pythonhosted.org/packages/74/a7/edce1403e05a46e59b502fae8e3350ceeac5841f8e8f1561e98562ed9b09/onnx-1.20.1-cp312-abi3-win32.whl", hash = "sha256:564c35a94811979808ab5800d9eb4f3f32c12daedba7e33ed0845f7c61ef2431", size = 16238216, upload-time = "2026-01-10T01:39:39.46Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c7/8690c81200ae652ac550c1df52f89d7795e6cc941f3cb38c9ef821419e80/onnx-1.20.1-cp312-abi3-win_amd64.whl", hash = "sha256:9fe7f9a633979d50984b94bda8ceb7807403f59a341d09d19342dc544d0ca1d5", size = 16389207, upload-time = "2026-01-10T01:39:41.955Z" }, + { url = "https://files.pythonhosted.org/packages/01/a0/4fb0e6d36eaf079af366b2c1f68bafe92df6db963e2295da84388af64abc/onnx-1.20.1-cp312-abi3-win_arm64.whl", hash = "sha256:21d747348b1c8207406fa2f3e12b82f53e0d5bb3958bcd0288bd27d3cb6ebb00", size = 16344155, upload-time = "2026-01-10T01:39:45.536Z" }, + { url = "https://files.pythonhosted.org/packages/ea/bb/715fad292b255664f0e603f1b2ef7bf2b386281775f37406beb99fa05957/onnx-1.20.1-cp313-cp313t-macosx_12_0_universal2.whl", hash = "sha256:29197b768f5acdd1568ddeb0a376407a2817844f6ac1ef8c8dd2d974c9ab27c3", size = 17912296, upload-time = "2026-01-10T01:39:48.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c3/541af12c3d45e159a94ee701100ba9e94b7bd8b7a8ac5ca6838569f894f8/onnx-1.20.1-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f0371aa67f51917a09cc829ada0f9a79a58f833449e03d748f7f7f53787c43c", size = 17416925, upload-time = "2026-01-10T01:39:50.82Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/d5660a7d2ddf14f531ca66d409239f543bb290277c3f14f4b4b78e32efa3/onnx-1.20.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be1e5522200b203b34327b2cf132ddec20ab063469476e1f5b02bb7bd259a489", size = 17515602, upload-time = "2026-01-10T01:39:54.132Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b4/47225ab2a92562eff87ba9a1a028e3535d659a7157d7cde659003998b8e3/onnx-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:15c815313bbc4b2fdc7e4daeb6e26b6012012adc4d850f4e3b09ed327a7ea92a", size = 16395729, upload-time = "2026-01-10T01:39:57.577Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7d/1bbe626ff6b192c844d3ad34356840cc60fca02e2dea0db95e01645758b1/onnx-1.20.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eb335d7bcf9abac82a0d6a0fda0363531ae0b22cfd0fc6304bff32ee29905def", size = 16348968, upload-time = "2026-01-10T01:40:00.491Z" }, +] + +[[package]] +name = "onnxconverter-common" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "onnx" }, + { name = "packaging" }, + { name = "protobuf" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/67/8dca1868a6e226f8d3f7d666cb6a48b79a60aad5267b16b24627cd8d9eb8/onnxconverter_common-1.16.0-py2.py3-none-any.whl", hash = "sha256:df39ee96f17fff119dff10dd245467651b60b9e8a96020eb93402239794852f7", size = 89511, upload-time = "2025-08-28T19:37:46.988Z" }, +] + +[[package]] +name = "onnxmltools" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "onnx" }, + { name = "protobuf" }, + { name = "skl2onnx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/b3/6537d1276c73dde84da74701347eed643188cf8a099f5f00f3bf522c8713/onnxmltools-1.15.0.tar.gz", hash = "sha256:99125770e515122126077fbb663bcdb164c9252ed78c871d5bbfe9ecf0aa32ba", size = 208082, upload-time = "2026-01-14T16:48:01.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/06/ff610f23ec135fc7ffeb11fe4da91ecf3b5de898b2d9c88e28126d6fae3e/onnxmltools-1.15.0-py3-none-any.whl", hash = "sha256:5eee1d2cee567fc9e58469291ebba7e01374e7eee46c33fa7c6e011550ec64f3", size = 303296, upload-time = "2026-01-14T16:47:59.583Z" }, +] + +[[package]] +name = "onnxruntime" +version = "1.24.0.dev20251031003" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coloredlogs" }, + { name = "flatbuffers" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "sympy" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/fc/cf4801f81e191704c038a9aa0e8104509cce527fca49b01e76cc9a25d9f0/onnxruntime-1.24.0.dev20251031003-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:91b1546d7ade331fbae68cd73a344dc8ed662d40e0c8a8b97c529353cc257c0b", size = 22576309, upload-time = "2025-10-31T22:21:44.568Z" }, + { url = "https://files.pythonhosted.org/packages/69/96/34169e84e2344dbbf938e072d7369a54c5d4f722b73bcaaf35bfe44b353c/onnxruntime-1.24.0.dev20251031003-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707b9f077c861a2b231b37d26bdb0fcf9493091d0a1dd7cc28ab6465270b3f0c", size = 23196395, upload-time = "2025-10-31T22:21:04.079Z" }, + { url = "https://files.pythonhosted.org/packages/06/c2/8782a120183ffa4f1e4ff265d3945d61ee43d4a2a267ec82b8aae67ef4c9/onnxruntime-1.24.0.dev20251031003-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c63d880ae4f9da817ff382902ab2bdb8b6036ed007cb55ad193b545a4eb4722", size = 23212561, upload-time = "2025-10-31T22:21:07.058Z" }, +] + [[package]] name = "opencv-python" version = "4.13.0.90" @@ -1350,6 +1545,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] +[[package]] +name = "protobuf" +version = "7.34.0rc1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/ec/f06d8a3f9b25efebf48c3442c317a4ddd545e032fd4566faa490d32197f2/protobuf-7.34.0rc1.tar.gz", hash = "sha256:5ceac3c2c428bfa5752b28082849fd9003db613b6c90305ec14bad6036a2d717", size = 454778, upload-time = "2026-01-22T20:23:35.355Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/82/5a9f71ad9de224df634348af68f2b52195b6823194dcf9ee409d61a9da5b/protobuf-7.34.0rc1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:74386406345f4d869da4a7906605a04295c1c904787992fb686ac321c9def6c3", size = 429263, upload-time = "2026-01-22T20:23:26.466Z" }, + { url = "https://files.pythonhosted.org/packages/69/40/c74464e5ca9fb8ed37bbe9223996d5db3f8790b6830baa66faefc315baf8/protobuf-7.34.0rc1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:7b42643d5ce4a9133084eec057d781e5e966b52bab68b9fdc6d7226384be931a", size = 325812, upload-time = "2026-01-22T20:23:28.668Z" }, + { url = "https://files.pythonhosted.org/packages/ed/17/2d0efc06f84bd29af485d67a94b5c35121943ea0a868b7876848f27905f2/protobuf-7.34.0rc1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:2250a68501df3e9381710e199771523056d442a1df1edeb3829d29e970224f68", size = 340240, upload-time = "2026-01-22T20:23:30.136Z" }, + { url = "https://files.pythonhosted.org/packages/15/a0/c06d82177587e5f62813c712b8ab41f428b22088cac477497319811e3061/protobuf-7.34.0rc1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:063194f132c92892dd271e9e77fc9651c84c56a486bb6e7657b99dde8462d3a5", size = 324296, upload-time = "2026-01-22T20:23:31.093Z" }, + { url = "https://files.pythonhosted.org/packages/bd/d7/42dc7c8f43de584578520a17aa84c84764e0af3bccf50080085a78158a32/protobuf-7.34.0rc1-cp310-abi3-win32.whl", hash = "sha256:2af017361d9ff1b52a4fe933fccf36bd5e453e3ef855dc66426ea03a006ad426", size = 426722, upload-time = "2026-01-22T20:23:32.48Z" }, + { url = "https://files.pythonhosted.org/packages/75/42/c9336c404347cb05711a342f6cc04c69bfdf3379b66e20ab2a135143bfb0/protobuf-7.34.0rc1-cp310-abi3-win_amd64.whl", hash = "sha256:d88119bf98ae532c2e92168471ddf2fdb3f3d3e58bf236be0c2af375d2b1b4d1", size = 437960, upload-time = "2026-01-22T20:23:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/80/70/74cd66938fd538017c2e152e9c231a2949c09302b986cde4550660669200/protobuf-7.34.0rc1-py3-none-any.whl", hash = "sha256:8c3c66f15e1035919bd105293d18c694986a7496ca105a1035a455da7b7958d2", size = 170811, upload-time = "2026-01-22T20:23:34.37Z" }, +] + [[package]] name = "psutil" version = "7.2.2" @@ -1423,6 +1633,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pymongo" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/65/9c/a4895c4b785fc9865a84a56e14b5bd21ca75aadc3dab79c14187cdca189b/pymongo-4.16.0.tar.gz", hash = "sha256:8ba8405065f6e258a6f872fe62d797a28f383a12178c7153c01ed04e845c600c", size = 2495323, upload-time = "2026-01-07T18:05:48.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/84/148d8b5da8260f4679d6665196ae04ab14ffdf06f5fe670b0ab11942951f/pymongo-4.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d15f060bc6d0964a8bb70aba8f0cb6d11ae99715438f640cff11bbcf172eb0e8", size = 972009, upload-time = "2026-01-07T18:04:38.303Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5e/9f3a8daf583d0adaaa033a3e3e58194d2282737dc164014ff33c7a081103/pymongo-4.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a19ea46a0fe71248965305a020bc076a163311aefbaa1d83e47d06fa30ac747", size = 971784, upload-time = "2026-01-07T18:04:39.669Z" }, + { url = "https://files.pythonhosted.org/packages/ad/f2/b6c24361fcde24946198573c0176406bfd5f7b8538335f3d939487055322/pymongo-4.16.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:311d4549d6bf1f8c61d025965aebb5ba29d1481dc6471693ab91610aaffbc0eb", size = 1947174, upload-time = "2026-01-07T18:04:41.368Z" }, + { url = "https://files.pythonhosted.org/packages/47/1a/8634192f98cf740b3d174e1018dd0350018607d5bd8ac35a666dc49c732b/pymongo-4.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46ffb728d92dd5b09fc034ed91acf5595657c7ca17d4cf3751322cd554153c17", size = 1991727, upload-time = "2026-01-07T18:04:42.965Z" }, + { url = "https://files.pythonhosted.org/packages/5a/2f/0c47ac84572b28e23028a23a3798a1f725e1c23b0cf1c1424678d16aff42/pymongo-4.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:acda193f440dd88c2023cb00aa8bd7b93a9df59978306d14d87a8b12fe426b05", size = 2082497, upload-time = "2026-01-07T18:04:44.652Z" }, + { url = "https://files.pythonhosted.org/packages/ba/57/9f46ef9c862b2f0cf5ce798f3541c201c574128d31ded407ba4b3918d7b6/pymongo-4.16.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d9fdb386cf958e6ef6ff537d6149be7edb76c3268cd6833e6c36aa447e4443f", size = 2064947, upload-time = "2026-01-07T18:04:46.228Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/5421c0998f38e32288100a07f6cb2f5f9f352522157c901910cb2927e211/pymongo-4.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91899dd7fb9a8c50f09c3c1cf0cb73bfbe2737f511f641f19b9650deb61c00ca", size = 1980478, upload-time = "2026-01-07T18:04:48.017Z" }, + { url = "https://files.pythonhosted.org/packages/92/93/bfc448d025e12313a937d6e1e0101b50cc9751636b4b170e600fe3203063/pymongo-4.16.0-cp313-cp313-win32.whl", hash = "sha256:2cd60cd1e05de7f01927f8e25ca26b3ea2c09de8723241e5d3bcfdc70eaff76b", size = 934672, upload-time = "2026-01-07T18:04:49.538Z" }, + { url = "https://files.pythonhosted.org/packages/96/10/12710a5e01218d50c3dd165fd72c5ed2699285f77348a3b1a119a191d826/pymongo-4.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3ead8a0050c53eaa55935895d6919d393d0328ec24b2b9115bdbe881aa222673", size = 959237, upload-time = "2026-01-07T18:04:51.382Z" }, + { url = "https://files.pythonhosted.org/packages/0c/56/d288bcd1d05bc17ec69df1d0b1d67bc710c7c5dbef86033a5a4d2e2b08e6/pymongo-4.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:dbbc5b254c36c37d10abb50e899bc3939bbb7ab1e7c659614409af99bd3e7675", size = 940909, upload-time = "2026-01-07T18:04:52.904Z" }, + { url = "https://files.pythonhosted.org/packages/30/9e/4d343f8d0512002fce17915a89477b9f916bda1205729e042d8f23acf194/pymongo-4.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:8a254d49a9ffe9d7f888e3c677eed3729b14ce85abb08cd74732cead6ccc3c66", size = 1026634, upload-time = "2026-01-07T18:04:54.359Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e3/341f88c5535df40c0450fda915f582757bb7d988cdfc92990a5e27c4c324/pymongo-4.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a1bf44e13cf2d44d2ea2e928a8140d5d667304abe1a61c4d55b4906f389fbe64", size = 1026252, upload-time = "2026-01-07T18:04:56.642Z" }, + { url = "https://files.pythonhosted.org/packages/af/64/9471b22eb98f0a2ca0b8e09393de048502111b2b5b14ab1bd9e39708aab5/pymongo-4.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f1c5f1f818b669875d191323a48912d3fcd2e4906410e8297bb09ac50c4d5ccc", size = 2207399, upload-time = "2026-01-07T18:04:58.255Z" }, + { url = "https://files.pythonhosted.org/packages/87/ac/47c4d50b25a02f21764f140295a2efaa583ee7f17992a5e5fa542b3a690f/pymongo-4.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77cfd37a43a53b02b7bd930457c7994c924ad8bbe8dff91817904bcbf291b371", size = 2260595, upload-time = "2026-01-07T18:04:59.788Z" }, + { url = "https://files.pythonhosted.org/packages/ee/1b/0ce1ce9dd036417646b2fe6f63b58127acff3cf96eeb630c34ec9cd675ff/pymongo-4.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:36ef2fee50eee669587d742fb456e349634b4fcf8926208766078b089054b24b", size = 2366958, upload-time = "2026-01-07T18:05:01.942Z" }, + { url = "https://files.pythonhosted.org/packages/3e/3c/a5a17c0d413aa9d6c17bc35c2b472e9e79cda8068ba8e93433b5f43028e9/pymongo-4.16.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55f8d5a6fe2fa0b823674db2293f92d74cd5f970bc0360f409a1fc21003862d3", size = 2346081, upload-time = "2026-01-07T18:05:03.576Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/f815533d1a88fb8a3b6c6e895bb085ffdae68ccb1e6ed7102202a307f8e2/pymongo-4.16.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9caacac0dd105e2555521002e2d17afc08665187017b466b5753e84c016628e6", size = 2246053, upload-time = "2026-01-07T18:05:05.459Z" }, + { url = "https://files.pythonhosted.org/packages/c6/88/4be3ec78828dc64b212c123114bd6ae8db5b7676085a7b43cc75d0131bd2/pymongo-4.16.0-cp314-cp314-win32.whl", hash = "sha256:c789236366525c3ee3cd6e4e450a9ff629a7d1f4d88b8e18a0aea0615fd7ecf8", size = 989461, upload-time = "2026-01-07T18:05:07.018Z" }, + { url = "https://files.pythonhosted.org/packages/af/5a/ab8d5af76421b34db483c9c8ebc3a2199fb80ae63dc7e18f4cf1df46306a/pymongo-4.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b0714d7764efb29bf9d3c51c964aed7c4c7237b341f9346f15ceaf8321fdb35", size = 1017803, upload-time = "2026-01-07T18:05:08.499Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f4/98d68020728ac6423cf02d17cfd8226bf6cce5690b163d30d3f705e8297e/pymongo-4.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:12762e7cc0f8374a8cae3b9f9ed8dabb5d438c7b33329232dd9b7de783454033", size = 997184, upload-time = "2026-01-07T18:05:09.944Z" }, + { url = "https://files.pythonhosted.org/packages/50/00/dc3a271daf06401825b9c1f4f76f018182c7738281ea54b9762aea0560c1/pymongo-4.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1c01e8a7cd0ea66baf64a118005535ab5bf9f9eb63a1b50ac3935dccf9a54abe", size = 1083303, upload-time = "2026-01-07T18:05:11.702Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4b/b5375ee21d12eababe46215011ebc63801c0d2c5ffdf203849d0d79f9852/pymongo-4.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4c4872299ebe315a79f7f922051061634a64fda95b6b17677ba57ef00b2ba2a4", size = 1083233, upload-time = "2026-01-07T18:05:13.182Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e3/52efa3ca900622c7dcb56c5e70f15c906816d98905c22d2ee1f84d9a7b60/pymongo-4.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:78037d02389745e247fe5ab0bcad5d1ab30726eaac3ad79219c7d6bbb07eec53", size = 2527438, upload-time = "2026-01-07T18:05:14.981Z" }, + { url = "https://files.pythonhosted.org/packages/cb/96/43b1be151c734e7766c725444bcbfa1de6b60cc66bfb406203746839dd25/pymongo-4.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c126fb72be2518395cc0465d4bae03125119136462e1945aea19840e45d89cfc", size = 2600399, upload-time = "2026-01-07T18:05:16.794Z" }, + { url = "https://files.pythonhosted.org/packages/e7/62/fa64a5045dfe3a1cd9217232c848256e7bc0136cffb7da4735c5e0d30e40/pymongo-4.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f3867dc225d9423c245a51eaac2cfcd53dde8e0a8d8090bb6aed6e31bd6c2d4f", size = 2720960, upload-time = "2026-01-07T18:05:18.498Z" }, + { url = "https://files.pythonhosted.org/packages/54/7b/01577eb97e605502821273a5bc16ce0fb0be5c978fe03acdbff471471202/pymongo-4.16.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f25001a955073b80510c0c3db0e043dbbc36904fd69e511c74e3d8640b8a5111", size = 2699344, upload-time = "2026-01-07T18:05:20.073Z" }, + { url = "https://files.pythonhosted.org/packages/55/68/6ef6372d516f703479c3b6cbbc45a5afd307173b1cbaccd724e23919bb1a/pymongo-4.16.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d9885aad05f82fd7ea0c9ca505d60939746b39263fa273d0125170da8f59098", size = 2577133, upload-time = "2026-01-07T18:05:22.052Z" }, + { url = "https://files.pythonhosted.org/packages/15/c7/b5337093bb01da852f945802328665f85f8109dbe91d81ea2afe5ff059b9/pymongo-4.16.0-cp314-cp314t-win32.whl", hash = "sha256:948152b30eddeae8355495f9943a3bf66b708295c0b9b6f467de1c620f215487", size = 1040560, upload-time = "2026-01-07T18:05:23.888Z" }, + { url = "https://files.pythonhosted.org/packages/96/8c/5b448cd1b103f3889d5713dda37304c81020ff88e38a826e8a75ddff4610/pymongo-4.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f6e42c1bc985d9beee884780ae6048790eb4cd565c46251932906bdb1630034a", size = 1075081, upload-time = "2026-01-07T18:05:26.874Z" }, + { url = "https://files.pythonhosted.org/packages/32/cd/ddc794cdc8500f6f28c119c624252fb6dfb19481c6d7ed150f13cf468a6d/pymongo-4.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6b2a20edb5452ac8daa395890eeb076c570790dfce6b7a44d788af74c2f8cf96", size = 1047725, upload-time = "2026-01-07T18:05:28.47Z" }, +] + [[package]] name = "pyparsing" version = "3.3.2" @@ -1432,6 +1683,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, ] +[[package]] +name = "pyreadline3" +version = "3.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839, upload-time = "2024-09-19T02:40:10.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178, upload-time = "2024-09-19T02:40:08.598Z" }, +] + [[package]] name = "pyright" version = "1.1.408" @@ -1656,6 +1916,56 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, ] +[[package]] +name = "scikit-image" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "imageio" }, + { name = "lazy-loader" }, + { name = "networkx" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "scipy" }, + { name = "tifffile" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/b4/2528bb43c67d48053a7a649a9666432dc307d66ba02e3a6d5c40f46655df/scikit_image-0.26.0.tar.gz", hash = "sha256:f5f970ab04efad85c24714321fcc91613fcb64ef2a892a13167df2f3e59199fa", size = 22729739, upload-time = "2025-12-20T17:12:21.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/48/02357ffb2cca35640f33f2cfe054a4d6d5d7a229b88880a64f1e45c11f4e/scikit_image-0.26.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a2e852eccf41d2d322b8e60144e124802873a92b8d43a6f96331aa42888491c7", size = 12346329, upload-time = "2025-12-20T17:11:11.599Z" }, + { url = "https://files.pythonhosted.org/packages/67/b9/b792c577cea2c1e94cda83b135a656924fc57c428e8a6d302cd69aac1b60/scikit_image-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:98329aab3bc87db352b9887f64ce8cdb8e75f7c2daa19927f2e121b797b678d5", size = 12031726, upload-time = "2025-12-20T17:11:13.871Z" }, + { url = "https://files.pythonhosted.org/packages/07/a9/9564250dfd65cb20404a611016db52afc6268b2b371cd19c7538ea47580f/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:915bb3ba66455cf8adac00dc8fdf18a4cd29656aec7ddd38cb4dda90289a6f21", size = 13094910, upload-time = "2025-12-20T17:11:16.2Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b8/0d8eeb5a9fd7d34ba84f8a55753a0a3e2b5b51b2a5a0ade648a8db4a62f7/scikit_image-0.26.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b36ab5e778bf50af5ff386c3ac508027dc3aaeccf2161bdf96bde6848f44d21b", size = 13660939, upload-time = "2025-12-20T17:11:18.464Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d6/91d8973584d4793d4c1a847d388e34ef1218d835eeddecfc9108d735b467/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:09bad6a5d5949c7896c8347424c4cca899f1d11668030e5548813ab9c2865dcb", size = 14138938, upload-time = "2025-12-20T17:11:20.919Z" }, + { url = "https://files.pythonhosted.org/packages/39/9a/7e15d8dc10d6bbf212195fb39bdeb7f226c46dd53f9c63c312e111e2e175/scikit_image-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aeb14db1ed09ad4bee4ceb9e635547a8d5f3549be67fc6c768c7f923e027e6cd", size = 14752243, upload-time = "2025-12-20T17:11:23.347Z" }, + { url = "https://files.pythonhosted.org/packages/8f/58/2b11b933097bc427e42b4a8b15f7de8f24f2bac1fd2779d2aea1431b2c31/scikit_image-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:ac529eb9dbd5954f9aaa2e3fe9a3fd9661bfe24e134c688587d811a0233127f1", size = 11906770, upload-time = "2025-12-20T17:11:25.297Z" }, + { url = "https://files.pythonhosted.org/packages/ad/ec/96941474a18a04b69b6f6562a5bd79bd68049fa3728d3b350976eccb8b93/scikit_image-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:a2d211bc355f59725efdcae699b93b30348a19416cc9e017f7b2fb599faf7219", size = 11342506, upload-time = "2025-12-20T17:11:27.399Z" }, + { url = "https://files.pythonhosted.org/packages/03/e5/c1a9962b0cf1952f42d32b4a2e48eed520320dbc4d2ff0b981c6fa508b6b/scikit_image-0.26.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9eefb4adad066da408a7601c4c24b07af3b472d90e08c3e7483d4e9e829d8c49", size = 12663278, upload-time = "2025-12-20T17:11:29.358Z" }, + { url = "https://files.pythonhosted.org/packages/ae/97/c1a276a59ce8e4e24482d65c1a3940d69c6b3873279193b7ebd04e5ee56b/scikit_image-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6caec76e16c970c528d15d1c757363334d5cb3069f9cea93d2bead31820511f3", size = 12405142, upload-time = "2025-12-20T17:11:31.282Z" }, + { url = "https://files.pythonhosted.org/packages/d4/4a/f1cbd1357caef6c7993f7efd514d6e53d8fd6f7fe01c4714d51614c53289/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a07200fe09b9d99fcdab959859fe0f7db8df6333d6204344425d476850ce3604", size = 12942086, upload-time = "2025-12-20T17:11:33.683Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6f/74d9fb87c5655bd64cf00b0c44dc3d6206d9002e5f6ba1c9aeb13236f6bf/scikit_image-0.26.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92242351bccf391fc5df2d1529d15470019496d2498d615beb68da85fe7fdf37", size = 13265667, upload-time = "2025-12-20T17:11:36.11Z" }, + { url = "https://files.pythonhosted.org/packages/a7/73/faddc2413ae98d863f6fa2e3e14da4467dd38e788e1c23346cf1a2b06b97/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:52c496f75a7e45844d951557f13c08c81487c6a1da2e3c9c8a39fcde958e02cc", size = 14001966, upload-time = "2025-12-20T17:11:38.55Z" }, + { url = "https://files.pythonhosted.org/packages/02/94/9f46966fa042b5d57c8cd641045372b4e0df0047dd400e77ea9952674110/scikit_image-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:20ef4a155e2e78b8ab973998e04d8a361d49d719e65412405f4dadd9155a61d9", size = 14359526, upload-time = "2025-12-20T17:11:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b4/2840fe38f10057f40b1c9f8fb98a187a370936bf144a4ac23452c5ef1baf/scikit_image-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c9087cf7d0e7f33ab5c46d2068d86d785e70b05400a891f73a13400f1e1faf6a", size = 12287629, upload-time = "2025-12-20T17:11:43.11Z" }, + { url = "https://files.pythonhosted.org/packages/22/ba/73b6ca70796e71f83ab222690e35a79612f0117e5aaf167151b7d46f5f2c/scikit_image-0.26.0-cp313-cp313t-win_arm64.whl", hash = "sha256:27d58bc8b2acd351f972c6508c1b557cfed80299826080a4d803dd29c51b707e", size = 11647755, upload-time = "2025-12-20T17:11:45.279Z" }, + { url = "https://files.pythonhosted.org/packages/51/44/6b744f92b37ae2833fd423cce8f806d2368859ec325a699dc30389e090b9/scikit_image-0.26.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:63af3d3a26125f796f01052052f86806da5b5e54c6abef152edb752683075a9c", size = 12365810, upload-time = "2025-12-20T17:11:47.357Z" }, + { url = "https://files.pythonhosted.org/packages/40/f5/83590d9355191f86ac663420fec741b82cc547a4afe7c4c1d986bf46e4db/scikit_image-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ce00600cd70d4562ed59f80523e18cdcc1fae0e10676498a01f73c255774aefd", size = 12075717, upload-time = "2025-12-20T17:11:49.483Z" }, + { url = "https://files.pythonhosted.org/packages/72/48/253e7cf5aee6190459fe136c614e2cbccc562deceb4af96e0863f1b8ee29/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6381edf972b32e4f54085449afde64365a57316637496c1325a736987083e2ab", size = 13161520, upload-time = "2025-12-20T17:11:51.58Z" }, + { url = "https://files.pythonhosted.org/packages/73/c3/cec6a3cbaadfdcc02bd6ff02f3abfe09eaa7f4d4e0a525a1e3a3f4bce49c/scikit_image-0.26.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6624a76c6085218248154cc7e1500e6b488edcd9499004dd0d35040607d7505", size = 13684340, upload-time = "2025-12-20T17:11:53.708Z" }, + { url = "https://files.pythonhosted.org/packages/d4/0d/39a776f675d24164b3a267aa0db9f677a4cb20127660d8bf4fd7fef66817/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f775f0e420faac9c2aa6757135f4eb468fb7b70e0b67fa77a5e79be3c30ee331", size = 14203839, upload-time = "2025-12-20T17:11:55.89Z" }, + { url = "https://files.pythonhosted.org/packages/ee/25/2514df226bbcedfe9b2caafa1ba7bc87231a0c339066981b182b08340e06/scikit_image-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede4d6d255cc5da9faeb2f9ba7fedbc990abbc652db429f40a16b22e770bb578", size = 14770021, upload-time = "2025-12-20T17:11:58.014Z" }, + { url = "https://files.pythonhosted.org/packages/8d/5b/0671dc91c0c79340c3fe202f0549c7d3681eb7640fe34ab68a5f090a7c7f/scikit_image-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:0660b83968c15293fd9135e8d860053ee19500d52bf55ca4fb09de595a1af650", size = 12023490, upload-time = "2025-12-20T17:12:00.013Z" }, + { url = "https://files.pythonhosted.org/packages/65/08/7c4cb59f91721f3de07719085212a0b3962e3e3f2d1818cbac4eeb1ea53e/scikit_image-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:b8d14d3181c21c11170477a42542c1addc7072a90b986675a71266ad17abc37f", size = 11473782, upload-time = "2025-12-20T17:12:01.983Z" }, + { url = "https://files.pythonhosted.org/packages/49/41/65c4258137acef3d73cb561ac55512eacd7b30bb4f4a11474cad526bc5db/scikit_image-0.26.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:cde0bbd57e6795eba83cb10f71a677f7239271121dc950bc060482834a668ad1", size = 12686060, upload-time = "2025-12-20T17:12:03.886Z" }, + { url = "https://files.pythonhosted.org/packages/e7/32/76971f8727b87f1420a962406388a50e26667c31756126444baf6668f559/scikit_image-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:163e9afb5b879562b9aeda0dd45208a35316f26cc7a3aed54fd601604e5cf46f", size = 12422628, upload-time = "2025-12-20T17:12:05.921Z" }, + { url = "https://files.pythonhosted.org/packages/37/0d/996febd39f757c40ee7b01cdb861867327e5c8e5f595a634e8201462d958/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724f79fd9b6cb6f4a37864fe09f81f9f5d5b9646b6868109e1b100d1a7019e59", size = 12962369, upload-time = "2025-12-20T17:12:07.912Z" }, + { url = "https://files.pythonhosted.org/packages/48/b4/612d354f946c9600e7dea012723c11d47e8d455384e530f6daaaeb9bf62c/scikit_image-0.26.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3268f13310e6857508bd87202620df996199a016a1d281b309441d227c822394", size = 13272431, upload-time = "2025-12-20T17:12:10.255Z" }, + { url = "https://files.pythonhosted.org/packages/0a/6e/26c00b466e06055a086de2c6e2145fe189ccdc9a1d11ccc7de020f2591ad/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fac96a1f9b06cd771cbbb3cd96c5332f36d4efd839b1d8b053f79e5887acde62", size = 14016362, upload-time = "2025-12-20T17:12:12.793Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/00a90402e1775634043c2a0af8a3c76ad450866d9fa444efcc43b553ba2d/scikit_image-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c1e7bd342f43e7a97e571b3f03ba4c1293ea1a35c3f13f41efdc8a81c1dc8f2", size = 14364151, upload-time = "2025-12-20T17:12:14.909Z" }, + { url = "https://files.pythonhosted.org/packages/da/ca/918d8d306bd43beacff3b835c6d96fac0ae64c0857092f068b88db531a7c/scikit_image-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b702c3bb115e1dcf4abf5297429b5c90f2189655888cbed14921f3d26f81d3a4", size = 12413484, upload-time = "2025-12-20T17:12:17.046Z" }, + { url = "https://files.pythonhosted.org/packages/dc/cd/4da01329b5a8d47ff7ec3c99a2b02465a8017b186027590dc7425cee0b56/scikit_image-0.26.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0608aa4a9ec39e0843de10d60edb2785a30c1c47819b67866dd223ebd149acaf", size = 11769501, upload-time = "2025-12-20T17:12:19.339Z" }, +] + [[package]] name = "scikit-learn" version = "1.8.0" @@ -1786,6 +2096,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "skl2onnx" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "onnx" }, + { name = "scikit-learn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/59/3f4d82eb96d8a80fd80325fc658d20e772ee6ac1c6f5cc85dd6aaaf7ccff/skl2onnx-1.19.1.tar.gz", hash = "sha256:0c105f2a3b87a624dd218d1fb98fdd19cf1bf6217190d25ce7e15484127d0e5d", size = 948919, upload-time = "2025-05-28T17:37:02.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/ec/9a0d709217aa385d87b3eadcf19e2ae32eca097077fa2236312d5fc8f656/skl2onnx-1.19.1-py3-none-any.whl", hash = "sha256:fddf2f49e3ffc355f332e676b43c6fec5e63797627925b279d9f5b2c4d0c81a7", size = 315511, upload-time = "2025-05-28T17:37:00.61Z" }, +] + [[package]] name = "sympy" version = "1.14.0" @@ -1807,6 +2130,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] +[[package]] +name = "tifffile" +version = "2026.1.28" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/32/38498d2a1a5d70f33f6c3909bbad48557c9a54b0e33a9307ff06b6d416ba/tifffile-2026.1.28.tar.gz", hash = "sha256:537ae6466a8bb555c336108bb1878d8319d52c9c738041d3349454dea6956e1c", size = 374675, upload-time = "2026-01-29T05:17:24.992Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/09/19/529b28ca338c5a88315e71e672badc85eef89460c248c4164f6ce058f8c7/tifffile-2026.1.28-py3-none-any.whl", hash = "sha256:45b08a19cf603dd99952eff54a61519626a1912e4e2a4d355f05938fe4a6e9fd", size = 233011, upload-time = "2026-01-29T05:17:23.078Z" }, +] + [[package]] name = "tokenizers" version = "0.22.2" From 06bdc724fcac6e758b5e8daaa3c29320a898b6de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CB=B6=F0=9D=9E=A2=E2=A4=AC=E2=AB=92=E2=B5=96s=E1=90=BC?= =?UTF-8?q?=CB=B6?= <91800957+exdysa@users.noreply.github.com> Date: Thu, 29 Jan 2026 22:05:54 -0500 Subject: [PATCH 3/3] ~close parenthesis --- negate/track.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/negate/track.py b/negate/track.py index 9c6c4ee..2cb0cab 100644 --- a/negate/track.py +++ b/negate/track.py @@ -80,5 +80,5 @@ def in_console(train_result: TrainResult) -> None: plt.ylabel("Explained Variance Ratio") plt.title("First 20 Components") plt.tight_layout() - plt.savefig(os.path.join("results", f"variance_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png") + plt.savefig(os.path.join("results", f"variance_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png")) plt.show()