Skip to content

Commit

Permalink
starts encapsulating image in Figure
Browse files Browse the repository at this point in the history
  • Loading branch information
dilawar committed Jul 25, 2024
1 parent 5f0a463 commit b59d3be
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 30 deletions.
44 changes: 36 additions & 8 deletions plotdigitizer/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,53 @@

from pathlib import Path
import typing as T
import logging
from loguru import logger

import cv2 as cv
import numpy as np
import numpy.typing as npt

from plotdigitizer import common
from plotdigitizer import geometry
from plotdigitizer import grid
from plotdigitizer.trajectory import find_trajectory

class Figure:
def __init__(self, path: Path):
assert path.exists(), f"{path} does not exists."
logger.info(f"Reading {path}")
self.path = path
self.orignal = cv.imread(self.path)
self.imgs = [("orig-gray-normalized", normalize(cv.imread(self.path, 0)))]

def remove_grid(self):
self._append("remove-grid", grid.remove_grid(self._last()))
save_img_in_cache(self._last(), Path(f"{self.path.name}.without_grid.png"))
self._append("normalize", normalize(self._last()))
img = self._last()
logger.debug(f" {img.min()=} {img.max()=}")
assert img.max() <= 255
assert img.min() < img.mean() < img.max(), "Could not read meaningful data"

def trajectories(self):
return process_image(self._last(), "_image")

def extract_trajectories(self):
logger.info(f"Extracting trajectories from {infile}")

def _last(self):
return self.imgs[-1][1]

def _append(self, operation: str, img):
self.imgs.append((operation, img))

def process_image(img, cache_key: T.Optional[str] = None):
global params_
global args_
common.params_ = compute_foregrond_background_stats(img)

T = transform_axis(img, erase_near_axis=3)
assert img.std() > 0.0, "No data in the image!"
logging.info(f" {img.mean()} {img.std()}")
logger.info(f" {img.mean()} {img.std()}")
if cache_key is not None:
save_img_in_cache(img, f"{cache_key}.transformed_axis.png")

Expand All @@ -43,7 +71,7 @@ def compute_foregrond_background_stats(img) -> T.Dict[str, T.Any]:
bgcolor, trajcolors = find_trajectory_colors(img)
params["background"] = bgcolor
params["timeseries_colors"] = trajcolors
logging.debug(f" computed parameters: {params}")
logger.debug(f" computed parameters: {params}")
return params


Expand Down Expand Up @@ -78,7 +106,7 @@ def find_trajectory_colors(

# we assume that bgcolor is close to white.
if bgcolor < 128:
logging.error(
logger.error(
"I computed that background is 'dark' which is unacceptable to me."
)
quit(-1)
Expand Down Expand Up @@ -108,10 +136,10 @@ def transform_axis(img, erase_near_axis: int = 0):
T = axis_transformation(common.points_, common.locations_)
p = geometry.find_origin(common.locations_)
offCols, offRows = p.x, p.y
logging.info(f"{common.locations_} → origin {offCols}, {offRows}")
logger.info(f"{common.locations_} → origin {offCols}, {offRows}")
img[:, : offCols + erase_near_axis] = common.params_["background"]
img[-offRows - erase_near_axis :, :] = common.params_["background"]
logging.debug(f"Tranformation params: {T}")
logger.debug(f"Tranformation params: {T}")
return T


Expand All @@ -122,7 +150,7 @@ def save_img_in_cache(
filename = Path(f"{common.data_to_hash(img)}.png")
outpath = common.cache() / filename
cv.imwrite(str(outpath), np.array(img))
logging.debug(f" Saved to {outpath}")
logger.debug(f" Saved to {outpath}")


# Thanks https://codereview.stackexchange.com/a/185794
Expand Down
31 changes: 10 additions & 21 deletions plotdigitizer/plotdigitizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from plotdigitizer import common

# Logger
import logging
from loguru import logger


app = typer.Typer()
Expand Down Expand Up @@ -47,7 +47,7 @@ def plot_traj(traj, outfile: Path):
plt.show()
else:
plt.savefig(outfile)
logging.info(f"Saved to {outfile}")
logger.info(f"Saved to {outfile}")
plt.close()


Expand All @@ -56,7 +56,7 @@ def click_points(event, x, y, _flags, params):
# Function to record the clicks.
YROWS = common.img_.shape[0]
if event == cv.EVENT_LBUTTONDOWN:
logging.info(f"You clicked on {(x, YROWS-y)}")
logger.info(f"You clicked on {(x, YROWS-y)}")
common.locations_.append(geometry.Point(x, YROWS - y))


Expand All @@ -80,7 +80,7 @@ def ask_user_to_locate_points(points, img):
key = cv.waitKey(1) & 0xFF
if key == "q":
break
logging.info("You clicked %s" % common.locations_)
logger.info("You clicked %s" % common.locations_)


def list_to_points(points) -> T.List[geometry.Point]:
Expand Down Expand Up @@ -122,35 +122,24 @@ def digitize_plot(
),
] = None,
):
assert infile.exists(), f"{infile} does not exists."
logging.info(f"Extracting trajectories from {infile}")

# reads into gray-scale.
common.img_ = image.normalize(cv.imread(str(infile), 0))
figure = image.Figure(infile)

# remove grids.
common.img_ = grid.remove_grid(common.img_)
image.save_img_in_cache(common.img_, Path(f"{infile.name}.without_grid.png"))

# rescale it again.
common.img_ = image.normalize(common.img_)
logging.debug(" {common.img_.min()=} {common.img_.max()=}")
assert common.img_.max() <= 255
assert common.img_.min() < common.img_.mean() < common.img_.max(), "Could not read meaningful data"
figure.remove_grid()
image.save_img_in_cache(common.img_, infile.name)

common.points_ = list_to_points(data_point)
common.locations_ = list_to_points(location)
logging.debug(f"data points {data_point} → location on image {location}")
logger.debug(f"data points {data_point} → location on image {location}")

if len(common.locations_) != len(common.points_):
logging.warning(
logger.warning(
"Either the location of data-points are not specified or their numbers don't"
" match with given datapoints. Asking user..."
)
ask_user_to_locate_points(common.points_, common.img_)

traj = image.process_image(common.img_, "_image")
traj = figure.trajectories()

if plot_file is not None:
plot_traj(traj, plot_file)
Expand All @@ -159,7 +148,7 @@ def digitize_plot(
with open(outfile, "w") as f:
for r in traj:
f.write("%g %g\n" % (r))
logging.info("Wrote trajectory to %s" % outfile)
logger.info("Wrote trajectory to %s" % outfile)


def main() -> T.Any:
Expand Down
34 changes: 33 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ numpy = "^2.0.0"
matplotlib = "^3.9.1"
typer = "^0.12.3"
typing-extensions = "^4.12.2"
loguru = "^0.7.2"

[tool.poetry.dev-dependencies]
pytest = "^8.2.2"
Expand Down

0 comments on commit b59d3be

Please sign in to comment.