Skip to content

Commit

Permalink
back2raw results to json and png
Browse files Browse the repository at this point in the history
fix #51 and #20
  • Loading branch information
HowcanoeWang committed Jan 25, 2023
1 parent 3fe9848 commit 7210b10
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 61 deletions.
Binary file added docs/_static/images/python_api/back2raw_save.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/images/python_api/back2raw_sort.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 46 additions & 1 deletion docs/jupyter/backward_projection.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,29 @@
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"<div class=\"alert alert-info\">\n",
"\n",
"Note\n",
"\n",
"You can save the results (json and cropped png) to given folder by:\n",
"\n",
"```python\n",
"img_dict_sort = roi.back2raw(ms, ..., save_folder=\"folder_to_save\")\n",
"```\n",
"\n",
"And will get the following results in the folder:\n",
"\n",
"<center><img src=\"../_static/images/python_api/back2raw_save.png\" alt=\"lotus plot\" width=\"50%\"/></center>\n",
"\n",
"</div>\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -548,6 +571,28 @@
")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"alert alert-info\">\n",
"\n",
"Note\n",
"\n",
"You can save the results (json and cropped png) to given folder by:\n",
"\n",
"```python\n",
"img_dict_sort = ms.sort_img_by_distance(..., save_folder=\"folder_to_save\")\n",
"```\n",
"\n",
"And will get the following results in the folder:\n",
"\n",
"<center><img src=\"../_static/images/python_api/back2raw_sort.png\" alt=\"lotus plot\" width=\"50%\"/></center>\n",
"\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": 14,
Expand Down Expand Up @@ -703,7 +748,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.13"
"version": "3.8.13 (default, Mar 28 2022, 06:16:26) \n[Clang 12.0.0 ]"
},
"vscode": {
"interpreter": {
Expand Down
15 changes: 15 additions & 0 deletions easyidp/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ def __init__(self, test_out="./tests/out"):

self.cv = self.CVDataset(self.data_dir, test_out)
self.vis = self.VisualDataset(self.data_dir, test_out)
self.b2r = self.Back2rawDataset(self.data_dir, test_out)


class MetashapeDataset():
Expand Down Expand Up @@ -646,3 +647,17 @@ def __init__(self, data_dir, test_out):

def __truediv__(self, other):
return self.data_dir / "visual_test" / other



class Back2rawDataset():

def __init__(self, data_dir, test_out):
self.data_dir = data_dir

if isinstance(test_out, str):
test_out = Path(test_out)
self.out = test_out / "back2raw_test"

def __truediv__(self, other):
return self.data_dir / "back2raw_test" / other
21 changes: 11 additions & 10 deletions easyidp/metashape.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def _back2raw_one2one(self, points_np, photo_id, distortion_correct=True):
else:
return out

def back2raw_crs(self, points_xyz, save_folder=None, ignore=None, log=False):
def back2raw_crs(self, points_xyz, ignore=None, log=False):
"""Projs one GIS coordintates ROI (polygon) to all images
Parameters
Expand All @@ -457,11 +457,6 @@ def back2raw_crs(self, points_xyz, save_folder=None, ignore=None, log=False):
- ``x``: only y (vertical) in image area, x can outside image;
- ``y``: only x (horizontal) in image area, y can outside image.
save_folder : str | default ""
The folder to contain the output results (preview images and json coords)
.. caution:: This feature has not been implemented
log : bool, optional
whether print log for debugging, by default False
Expand Down Expand Up @@ -543,7 +538,7 @@ def back2raw(self, roi, save_folder=None, **kwargs):
roi : easyidp.ROI | dict
the <ROI> object created by easyidp.ROI() or dictionary
save_folder : str, optional
the folder to save projected preview images and json files, by default ""
the folder to save json files and parts of ROI on raw images, by default None
ignore : str | None, optional
Whether tolerate small parts outside image, check
:func:`easyidp.reconstruct.Sensor.in_img_boundary` for more details.
Expand Down Expand Up @@ -619,11 +614,15 @@ def back2raw(self, roi, save_folder=None, **kwargs):
if points_xyz.shape[1] != 3:
raise ValueError(f"The back2raw function requires 3D roi with shape=(n, 3), but [{k}] is {points_xyz.shape}")

one_roi_dict= self.back2raw_crs(points_xyz, save_folder=save_path, **kwargs)
one_roi_dict= self.back2raw_crs(points_xyz, **kwargs)

out_dict[k] = one_roi_dict

self.crs = before_crs

if save_folder is not None:
idp.reconstruct.save_back2raw_json_and_png(self, out_dict, save_folder)

return out_dict

def get_photo_position(self, to_crs=None, refresh=False):
Expand Down Expand Up @@ -714,7 +713,7 @@ def get_photo_position(self, to_crs=None, refresh=False):
return out


def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None):
def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None, save_folder=None):
"""Advanced wrapper of sorting back2raw img_dict results by distance from photo to roi
Parameters
Expand All @@ -728,6 +727,8 @@ def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None
Keep the closest {x} images
distance_thresh : None or float
Keep the images closer than this distance to ROI.
save_folder : str, optional
the folder to save json files and parts of ROI on raw images, by default None
Returns
-------
Expand Down Expand Up @@ -851,7 +852,7 @@ def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None
if not self.enabled:
raise TypeError("Unable to process disabled chunk (.enabled=False)")

return idp.reconstruct.sort_img_by_distance(self, img_dict_all, roi, distance_thresh, num)
return idp.reconstruct.sort_img_by_distance(self, img_dict_all, roi, distance_thresh, num, save_folder)

def show_roi_on_img(self, img_dict, roi_name, img_name=None, **kwargs):
"""Visualize the specific backward projection results for given roi on the given image.
Expand Down
29 changes: 10 additions & 19 deletions easyidp/pix4d.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ def _pmatrix_calc(self, points, photo, distort_correct=True):
return coords_b


def back2raw_crs(self, points_xyz, distort_correct=True, ignore=None, save_folder=None, log=False):
def back2raw_crs(self, points_xyz, distort_correct=True, ignore=None, log=False):
"""Projects one GIS coordintates ROI (polygon) to all images
Parameters
Expand All @@ -538,11 +538,6 @@ def back2raw_crs(self, points_xyz, distort_correct=True, ignore=None, save_folde
- ``x``: only y (vertical) in image area, x can outside image;
- ``y``: only x (horizontal) in image area, y can outside image.
save_folder : str | default ""
The folder to contain the output results (preview images and json coords)
.. caution:: This feature has not been implemented
log : bool, optional
whether print log for debugging, by default False
Expand Down Expand Up @@ -606,13 +601,6 @@ def back2raw_crs(self, points_xyz, distort_correct=True, ignore=None, save_folde
if coords is not None:
out_dict[photo.label] = coords

if isinstance(save_folder, str) and os.path.isdir(save_folder):
# if not os.path.exists(save_folder):
# os.makedirs(save_folder)
# save to json file
# save to one image file ()
raise NotImplementedError("This feature has not been implemented")

return out_dict


Expand Down Expand Up @@ -698,10 +686,13 @@ def back2raw(self, roi, save_folder=None, **kwargs):
f"The back2raw function requires 3D roi with shape=(n, 3)"
f", but [{k}] is {points_xyz.shape}")

one_roi_dict= self.back2raw_crs(points_xyz, save_folder=save_path, **kwargs)
one_roi_dict= self.back2raw_crs(points_xyz, **kwargs)

out_dict[k] = one_roi_dict

if save_folder is not None:
idp.reconstruct.save_back2raw_json_and_png(self, out_dict, save_folder)

return out_dict

def get_photo_position(self, to_crs=None, refresh=False):
Expand Down Expand Up @@ -731,9 +722,7 @@ def get_photo_position(self, to_crs=None, refresh=False):
>>> import easyidp as idp
>>> lotus = idp.data.Lotus()
>>> p4d = idp.Pix4D(project_path=lotus.pix4d.project,
>>> raw_img_folder=lotus.photo,
>>> param_folder=lotus.pix4d.param)
>>> p4d = idp.Pix4D(lotus.pix4d.project,lotus.photo, lotus.pix4d.param)
Then use this function to get the photo position in 3D world:
Expand Down Expand Up @@ -774,7 +763,7 @@ def get_photo_position(self, to_crs=None, refresh=False):

return out

def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None):
def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None, save_folder=None):
"""Advanced wrapper of sorting back2raw img_dict results by distance from photo to roi
Parameters
Expand All @@ -788,6 +777,8 @@ def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None
Keep the closest {x} images
distance_thresh : None or float
Keep the images closer than this distance to ROI.
save_folder : str, optional
the folder to save json files and parts of ROI on raw images, by default None
Returns
-------
Expand Down Expand Up @@ -913,7 +904,7 @@ def sort_img_by_distance(self, img_dict_all, roi, distance_thresh=None, num=None
easyidp.reconstruct.sort_img_by_distance
"""
return idp.reconstruct.sort_img_by_distance(self, img_dict_all, roi, distance_thresh, num)
return idp.reconstruct.sort_img_by_distance(self, img_dict_all, roi, distance_thresh, num, save_folder)

def show_roi_on_img(self, img_dict, roi_name, img_name=None, **kwargs):
"""Visualize the specific backward projection results for given roi on the given image.
Expand Down
86 changes: 84 additions & 2 deletions easyidp/reconstruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path
from tqdm import tqdm
import warnings
from skimage.io import imread, imsave

import easyidp as idp

Expand Down Expand Up @@ -649,7 +650,7 @@ def _sort_img_by_distance_one_roi(recons, img_dict, plot_geo, cam_pos, distance_
return img_dict_sort


def sort_img_by_distance(recons, img_dict_all, roi, distance_thresh=None, num=None):
def sort_img_by_distance(recons, img_dict_all, roi, distance_thresh=None, num=None, save_folder=None):
"""Advanced wrapper of sorting back2raw img_dict results by distance from photo to roi
Parameters
Expand All @@ -665,6 +666,8 @@ def sort_img_by_distance(recons, img_dict_all, roi, distance_thresh=None, num=No
Keep the closest {x} images
distance_thresh : None or float
Keep the images closer than this distance to ROI.
save_folder : str, optional
the folder to save json files and parts of ROI on raw images, by default None
Returns
-------
Expand All @@ -682,4 +685,83 @@ def sort_img_by_distance(recons, img_dict_all, roi, distance_thresh=None, num=No
)
img_dict_sort_all[roi_name] = sort_dict

return img_dict_sort_all
if save_folder is not None:
save_back2raw_json_and_png(recons, img_dict_sort_all, save_folder)

return img_dict_sort_all


def save_back2raw_json_and_png(recons, results_dict, save_folder):
"""Save the backward reversed results
Parameters
----------
results_dict : dict
the outputs of :func:`back2raw() <easyidp.roi.back2raw()>` function results
save_path : str
the folder to save output files
Example
-------
Data prepare
.. code-block:: python
>>> import easyidp as idp
>>> lotus = idp.data.Lotus()
>>> p4d = idp.Pix4D(lotus.pix4d.project, lotus.photo, lotus.pix4d.param)
>>> roi = idp.ROI(lotus.shp, name_field=0)
>>> roi = roi[0:3]
>>> roi.get_z_from_dsm(lotus.pix4d.dsm)
>>> out_p4d = roi.back2raw(p4d)
Use this function:
.. code-block:: python
>>> idp.reconstruct.save_back2raw_json_and_png(p4d, out_p4d, "out_path")
"""
# prepare the root folder
if isinstance(save_folder, (str, Path)):
save_folder = str(save_folder)
if not os.path.exists(save_folder):
os.makedirs(save_folder)
else:
raise TypeError(f"Only the string path is acceptable, not [{save_folder} {type(save_folder)}]")

# reverse the current order results_dict[roi][image_name] to results[image_name][roi]
# this will save the IO cost when loading images.
print("Optimising data structures of produced results, this may take some time...")
rev_dict = {}
for roi_name, roi_dict in results_dict.items():
# create the save path of each roi
roi_folder = os.path.join(save_folder, roi_name)
if not os.path.exists(roi_folder):
os.mkdir(roi_folder)

for img_name, img_pos in roi_dict.items():
if img_name not in rev_dict.keys():
rev_dict[img_name] = {}

rev_dict[img_name][roi_name] = img_pos

# save the full results as json directly
idp.jsonfile.save_json(results_dict, os.path.join(save_folder, "roi_image_order.json"))
idp.jsonfile.save_json(rev_dict, os.path.join(save_folder, "image_roi_order.json"))

# then doing the for loop for each image.
for img_name, img_rois in tqdm(rev_dict.items(), desc=f"Processing image [{img_name}]"):

img_array = imread(recons.photos[img_name].path)

for roi_name, img_pos in tqdm(img_rois.items(), leave=False):

png_save_path = os.path.join(save_folder, roi_name, f"{roi_name}_{img_name}.png")

cropped_png, _ = idp.cvtools.imarray_crop(img_array, img_pos)

imsave(png_save_path, cropped_png)
7 changes: 3 additions & 4 deletions easyidp/roi.py
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@ def crop(self, target, save_folder=None):

return out

def back2raw(self, recons, save_folder=None, **kwargs):
def back2raw(self, recons, **kwargs):
"""Projects several GIS coordintates ROIs (polygons) to all images
Parameters
Expand Down Expand Up @@ -948,9 +948,7 @@ def back2raw(self, recons, save_folder=None, **kwargs):
>>> import easyidp as idp
>>> lotus = idp.data.Lotus()
>>> p4d = idp.Pix4D(project_path=lotus.pix4d.project,
... raw_img_folder=lotus.photo,
... param_folder=lotus.pix4d.param)
>>> p4d = idp.Pix4D(lotus.pix4d.project, lotus.photo, lotus.pix4d.param)
>>> ms = idp.Metashape(project_path=lotus.metashape.project, chunk_id=0)
Expand Down Expand Up @@ -1090,6 +1088,7 @@ def load_detections(path):
"""

# boxes = pd.read_csv(path)
boxes = None
if not all([x in ["image_path","xmin","ymin","xmax","ymax","image_path","label"] for x in boxes.columns]):
raise IOError("{} is expected to be a .csv with columns, xmin, ymin, xmax, ymax, image_path, label for each detection")

Expand Down
Loading

0 comments on commit 7210b10

Please sign in to comment.