Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SCM syntax highlighting & preventing 3-way merges
pixi.lock merge=binary linguist-language=YAML linguist-generated=true
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ __pycache__
env
data
project
!project/*.sh
!project/*.sh# pixi environments
.pixi/*
!.pixi/config.toml
4,328 changes: 759 additions & 3,569 deletions pixi.lock

Large diffs are not rendered by default.

34 changes: 0 additions & 34 deletions pixi.toml

This file was deleted.

45 changes: 19 additions & 26 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
[tool.poetry]
[project]
dependencies = ["scikit-learn>=1.7.2,<2", "typer>=0.14.0", "pandas>=1.5.3", "opencv-python-headless>=4.11.0.86", "pyheif>=0.8.0", "pillow>=11.3.0"]
name = "smartsensor"
requires-python = ">=3.9"
version = "1.1.0"
description = "The sensor utilizes a smartphone camera to analyze and characterize chemical compounds."
readme = "README.md"
authors = ["Thanh-Giang (River) Tan Nguyen <nttg8100@gmail.com>", "Dung Cao <dung.cao@ttu.edu.vn>"]
license = "MIT"
homepage = "https://github.com/riverxdata/smartsensor"
repository = "https://github.com/riverxdata/smartsensor"
keywords = ["machine learning", "sensor", "smartphone"]

classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent"
]
[tool.poetry.dependencies]
python = ">=3.9"
typer = ">=0.14.0"
scikit-learn = ">=1.6.1"
pandas = ">=1.5.3"
opencv-python-headless = ">=4.11.0.86"
streamlit = ">= 1.45.1"
pyheif = ">=0.8.0"
pillow = ">=11.3.0"
[tool.poetry.scripts]
[build-system]
build-backend = "hatchling.build"
requires = ["hatchling"]

[tool.pixi.workspace]
channels = ["conda-forge", "bioconda"]
platforms = ["linux-64"]

[project.scripts]
smartsensor = "smartsensor.main:app"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.pixi.pypi-dependencies]
smartsensor = { path = ".", editable = true }

[tool.pixi.tasks]

[tool.pixi.dependencies]
libheif = ">=1.19.7,<2"
4 changes: 4 additions & 0 deletions smartsensor/e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from smartsensor.model.train import fit
from smartsensor.model.metric import evaluate_metrics, aggregate_replication_metrics
import pandas as pd
import numpy as np
import os
import tempfile
import warnings
Expand All @@ -23,7 +24,10 @@ def end2end_pipeline(
prefix: str,
test_size: float = 0.2,
replication: int = 1000,
seed: int = 1,
):
# set seed for reproducibility
np.random.seed(seed)

metrics = []
features = features.split(",")
Expand Down
12 changes: 6 additions & 6 deletions smartsensor/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
from smartsensor.process.any2jpg import heic2jpg as convert_heic_to_jpg


app = typer.Typer(
help="Smart Optical Sensor: Using smartphone camera as sensor", add_completion=False
)
app = typer.Typer(help="Smart Optical Sensor: Using smartphone camera as sensor", add_completion=False)


@app.command()
Expand Down Expand Up @@ -38,12 +36,11 @@ def model(
"--cv",
help="Fold for cross validation",
),
test_size: float = typer.Option(
0.2, "--test-size", help="Test data size for splitting to train the model"
),
test_size: float = typer.Option(0.2, "--test-size", help="Test data size for splitting to train the model"),
degree: int = typer.Option([1], "--degree", help="Degree of polynomial regression"),
replication: int = typer.Option(100, "--replication", help="Number of replication"),
out: str = typer.Option(".", help="Folder to save model"),
seed: int = typer.Option(1, "--seed", help="Random seed for reproducibility"),
):
"""
Run the end-to-end model for Smart Optical Sensor.
Expand All @@ -58,6 +55,7 @@ def model(
logger.info(f"Test size: {test_size}")
logger.info(f"Degree: {degree}")
logger.info(f"Output folder: {out}")
logger.info(f"Random seed: {seed}")
os.makedirs(out, exist_ok=True)
end2end_pipeline(
data=data,
Expand All @@ -71,6 +69,7 @@ def model(
prefix=prefix,
test_size=test_size,
replication=replication,
seed=seed,
)


Expand All @@ -84,6 +83,7 @@ def process(
help="Folder to save processed images",
),
process_dir: str = typer.Option(
None,
"--process-dir",
help="Path to the processed to get config for base background color",
),
Expand Down
3 changes: 0 additions & 3 deletions smartsensor/model/formula.py

This file was deleted.

10 changes: 3 additions & 7 deletions smartsensor/model/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def evaluate_metrics(
data: DataFrame,
features: List,
degree: int,
) -> (DataFrame, DataFrame):
) -> tuple[DataFrame, DataFrame]:
"""Simple evaluation matrics for measure the errors
Comment thread
nttg8100 marked this conversation as resolved.

Args:
Expand Down Expand Up @@ -41,12 +41,8 @@ def evaluate_metrics(
detail = detail.T
detail.columns = ["image", "expected_concentration", "predicted_concentration"]
detail = detail.sort_values("expected_concentration")
detail["absolute_error"] = abs(
detail["predicted_concentration"] - detail["expected_concentration"]
)
detail["error"] = (
detail["predicted_concentration"] - detail["expected_concentration"]
)
detail["absolute_error"] = abs(detail["predicted_concentration"] - detail["expected_concentration"])
detail["error"] = detail["predicted_concentration"] - detail["expected_concentration"]

return (metric, detail)

Expand Down
71 changes: 0 additions & 71 deletions smartsensor/model/partition.py

This file was deleted.

7 changes: 3 additions & 4 deletions smartsensor/model/split_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,21 @@ def split_data(
prefix: str,
test_size: float = 0.2,
):
"""It will combine the features and metadata, split to prepare for training and testing
"""It will combine the features and metadata, split to prepare for training and testing according to concentration.

Args:
meta_data(pd.DataFrame): The dataframe with data and metadata
outdir (str): The output directory for train and test csv file
prefix (str): The prefix to specify csv file
random_state (int, optional): To reproduce split. Defaults to 1.
test_size (float, optional): The test size if train, test batch is not specified. Defaults to 0.2.
"""
# train test split data
# Train test split data
# Define train and test data holders
train_data = pd.DataFrame()
test_data = pd.DataFrame()
# split dataset by batch equally
if test_size != 0:
for dataset_name, group_df in meta_data.groupby("concentration"):
for _, group_df in meta_data.groupby("concentration"):
train_subset, test_subset = train_test_split(group_df, test_size=test_size)
Comment thread
nttg8100 marked this conversation as resolved.
train_data = pd.concat([train_data, train_subset])
test_data = pd.concat([test_data, test_subset])
Expand Down
16 changes: 4 additions & 12 deletions smartsensor/process/normalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,15 @@ def normalize(
with open(os.path.join(outdir, "config.json"), "w") as f:
json.dump({"lum": lum}, f)

with open(failed_ratio_file, "w") as f_ratio, open(
failed_delta_file, "w"
) as f_delta:
with open(failed_ratio_file, "w") as f_ratio, open(failed_delta_file, "w") as f_delta:
f_ratio.write("image\n")
f_delta.write("image\n")

for image_location in glob.glob(os.path.join(raw_roi, "*.jpg")):
file_name = os.path.basename(image_location)
background_image_path = os.path.join(background, file_name)
if not os.path.exists(background_image_path):
raise ValueError(
f"Background image {background_image_path} does not exist."
)
raise ValueError(f"Background image {background_image_path} does not exist.")
raw_roi_img = cv2.imread(image_location)
background_img = cv2.imread(background_image_path)
logger.info(
Expand Down Expand Up @@ -194,7 +190,7 @@ def normalize_delta(
deltaG = lum[1] - G
deltaR = lum[2] - R

tmp = roi_image
tmp = roi_image.astype(np.float64)
tmp[:, :, 0] += deltaB
tmp[:, :, 1] += deltaG
tmp[:, :, 2] += deltaR
Expand All @@ -219,11 +215,7 @@ def normalize_delta(

logger.info(f"Normalize delta: {deltaB, deltaG, deltaR}")

if (
abs(deltaB) > THRESHOLD_DELTA
or abs(deltaG) > THRESHOLD_DELTA
or abs(deltaR) > THRESHOLD_DELTA
):
if abs(deltaB) > THRESHOLD_DELTA or abs(deltaG) > THRESHOLD_DELTA or abs(deltaR) > THRESHOLD_DELTA:
return True

return False