├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── R ├── combine_outlines_function.R ├── get_outlines_function.R ├── open_outlines_from_closed_outlines_v3.R ├── separate_single_artefacts_function.R └── tps_to_dataframe_function.R ├── README.md ├── man ├── combine_outlines.Rd ├── get_outlines.Rd ├── open_outlines_from_closed_outlines.Rd ├── separate_single_artefacts.Rd └── tps_to_df.Rd ├── outlineR.Rproj ├── playground ├── open_outlines_from_closed_outlines.R ├── open_outlines_from_closed_outlines_v2.R └── open_outlines_from_closed_outlines_v3.R └── test_data ├── README.md ├── derived_data ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_1.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_10.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_11.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_12.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_13.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_14.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_15.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_16.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_17.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_18.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_19.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_2.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_20.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_21.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_22.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_23.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_24.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_25.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_26.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_27.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_28.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_3.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_4.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_5.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_6.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_7.jpg ├── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_8.jpg └── Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_9.jpg ├── input_data ├── Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg └── tps_file.tps ├── panel_outlines_combined.jpeg ├── raw_data └── Morar_Quartz_Industry_Wellcome_Collection.jpeg ├── screenshot_derived_data.png └── stack_outlines_combined.jpeg /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^outlineR\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^\./test_data$ 4 | ^test_data$ 5 | ^data-raw$ 6 | ^playground$ 7 | ^LICENSE\.md$ 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | .Rhistory 3 | .Rapp.history 4 | 5 | # Session Data files 6 | .RData 7 | 8 | # User-specific files 9 | .Ruserdata 10 | 11 | # Example code in package build process 12 | *-Ex.R 13 | 14 | 15 | # gitignore 16 | .gitignore 17 | 18 | # Output files from R CMD build 19 | /*.tar.gz 20 | 21 | # Output files from R CMD check 22 | /*.Rcheck/ 23 | 24 | # RStudio files 25 | .Rproj.user/ 26 | 27 | # produced vignettes 28 | vignettes/*.html 29 | vignettes/*.pdf 30 | 31 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 32 | .httr-oauth 33 | 34 | # knitr and R markdown default cache directories 35 | *_cache/ 36 | /cache/ 37 | 38 | # Temporary files created by R markdown 39 | *.utf8.md 40 | *.knit.md 41 | 42 | # R Environment Variables 43 | .Renviron 44 | .Rproj.user 45 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: outlineR 2 | Title: An R package to derive outline shapes from (multiple) artefacts on JPEG images 3 | Version: 0.1.0 4 | Authors@R: 5 | c(person(given = "David Nicolas", 6 | family = "Matzig", 7 | role = c("aut", "cre", "cph"), 8 | email = "matzigdavid@gmail.com", 9 | comment = c(ORCID = "0000-0001-7349-5401")), 10 | person(given = "Clemens", 11 | family = "Schmid", 12 | role = c("ctb"), 13 | email = "clemens@nevrome.de", 14 | comment = c(ORCID = "0000-0003-3448-5715"))) 15 | Description: This package is a helpful wrapper around functions from mainly the Momocs (Bonhomme et al. 2014), EBImage (Pau et al. 2010), and imager (Barthelme et al. 2020) packages. It is designed for the fast and easy extraction of single outline shapes of, for example, stone tools from images containing multiple thereof, such as the ones present in archaeological publications. 16 | License: MIT + file LICENSE 17 | Encoding: UTF-8 18 | LazyData: true 19 | Roxygen: list(markdown = TRUE) 20 | RoxygenNote: 7.1.1 21 | Imports: 22 | EBImage, 23 | imager, 24 | Momocs, 25 | utils 26 | Remotes: bioc::release/EBImage 27 | SystemRequirements: fftw3 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2021 2 | COPYRIGHT HOLDER: David Nicolas Matzig 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2021 David Nicolas Matzig 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(combine_outlines) 4 | export(get_outlines) 5 | export(open_outlines_from_closed_outlines) 6 | export(separate_single_artefacts) 7 | export(tps_to_df) 8 | -------------------------------------------------------------------------------- /R/combine_outlines_function.R: -------------------------------------------------------------------------------- 1 | #' combine_outlines 2 | #' 3 | #' A function containing a recursive loop to combine a list of single 4 | #' outline coordinate-sets into a single Momocs::Out/Opn file 5 | #' 6 | #' @param single_outlines_list A list containing separate coordinate 7 | #' matrices (Momocs' Coo objects) as for example created with the 8 | #' separate_single_artefacts function. 9 | #' 10 | #' @return Returns the combined Coo objects in a single Out/Opn file 11 | #' 12 | #' @export 13 | combine_outlines <- function(single_outlines_list) { 14 | outlines_combined <- Momocs::combine(single_outlines_list[[1]], single_outlines_list[[2]]) 15 | 16 | for (outlines_index in 3:length(single_outlines_list)) { 17 | outlines_combined <- Momocs::combine(outlines_combined, single_outlines_list[[outlines_index]]) 18 | } 19 | 20 | return(outlines_combined) 21 | } 22 | -------------------------------------------------------------------------------- /R/get_outlines_function.R: -------------------------------------------------------------------------------- 1 | #' get_outlines 2 | #' 3 | #' Uses Momocs::import_jpg() to import outline coordinates from the single 4 | #' .jpg files which are either already avaiable, or were created using the 5 | #' separate_single_artefacts function. This function preserves the 6 | #' filenames. 7 | #' 8 | #' @param outpath The path to the folder containing the .jpg files of the singled out artefacts 9 | #' from which the outlines should be derived (as for example created in the 10 | #' separate_single_artefacts function). 11 | #' 12 | #' @param tps_file_rescale (Default = NULL) A dataframe containing at least a column _IMAGE_ and a column _SCALE_. 13 | #' This dataframe has to be created first outside this function using the function *tps_to_df* (for more information see ?tps_to_df()). 14 | #' _IMAGE_ contains the names of the images, as they are written in *inpath*. 15 | #' _SCALE_ contains the scaling factor from pixel to a metric measurement. 16 | #' Such a file can be created in i.e. tpsDIG2 (Rohlf 2017). 17 | #' 18 | #' @return Returns a Momocs Out file containing the outlines' coordinates 19 | #' with their associated IDs, derived from the file name. 20 | #' 21 | #' 22 | #' @example 23 | #' \dontrun{ 24 | #' get_outlines(outpath = pathname_output, tps_file_rescale = "./test_data/input_data/tps_file.tps") 25 | #'} 26 | #' 27 | #' @export 28 | get_outlines <- function(outpath, tps_file_rescale = NULL) { 29 | artefact_names <- list.files(outpath, 30 | pattern = c(".jpg", ".jpeg", ".JPG", ".JPEG"), 31 | full.names = F 32 | ) 33 | 34 | outlines <- list() 35 | 36 | pb2 <- utils::txtProgressBar(min = 0, max = length(artefact_names), style = 3) 37 | for (input_counter in 1:length(artefact_names)) { 38 | 39 | outline_coordinates <- Momocs::import_jpg(file.path(outpath, artefact_names[input_counter])) 40 | out_file_single_outline <- Momocs::Out(outline_coordinates) 41 | 42 | if (!is.null(tps_file_rescale)) { # is a TPS file provided? 43 | tps_file_rescale_df <- tps_file_rescale 44 | 45 | # Rescale coordinates from pixels to real length units 46 | if(nrow(subset(tps_file_rescale_df, # is the current image listed in the TPS file? 47 | tps_file_rescale_df$IMAGE == strsplit(artefact_names[input_counter], 48 | split = "_pseudo")[[1]][1])) == 1){ 49 | current_tps_dataset <- subset(tps_file_rescale_df, 50 | tps_file_rescale_df$IMAGE == strsplit(artefact_names[input_counter], 51 | split = "_pseudo")[[1]][1]) 52 | 53 | if(is.na(current_tps_dataset$SCALE) == FALSE){ # does the current image have a valid scaling factor? 54 | # rescale using rescale factor 55 | out_file_single_outline <- Momocs::rescale(out_file_single_outline, 56 | scaling_factor = current_tps_dataset$SCALE) 57 | } 58 | } 59 | 60 | } 61 | 62 | names(out_file_single_outline) <- strsplit(artefact_names[input_counter], 63 | split = "[.]")[[1]][1] 64 | outlines[[gsub("-", "_", strsplit(artefact_names[input_counter], 65 | split = "[.]")[[1]][1])]] <- out_file_single_outline 66 | 67 | utils::setTxtProgressBar(pb2, input_counter) 68 | } 69 | close(pb2) 70 | 71 | return(outlines) 72 | } 73 | -------------------------------------------------------------------------------- /R/open_outlines_from_closed_outlines_v3.R: -------------------------------------------------------------------------------- 1 | #' open_outlines_from_closed_outlines 2 | #' 3 | #' A function to create open outlines from closed outlines. 4 | #' The way it is implemented, the open outline will start at the highest, left-most coordinate, 5 | #' run clock-wise, and will end at the lowest, left-most coordinate. 6 | #' The starting point is found using the shortest distance from an arificial point on X = 0 and Y = max(current_outline_df$Y) (i.e., the overall highest Y-coordinate), 7 | #' to the artefacts outline. The ending point is found using the starting points X-coordinate and it's corresponding minimum Y-coordinate. 8 | #' 9 | #' This works only for artefacts, which have their supposed open side on their left and have been prepared in a way, so that this open left side is a straigt vertical line. 10 | #' 11 | #' @param outlines_combined A Momocs Out file containing closed outlines. 12 | #' @param return_combined_outlines (default = TRUE) A logical parameter stating wether to output a Momocs Opn-file, or a list of coordinate matrices of each open outline. 13 | #' @return If return_combined_outlines = TRUE, returns the combined Coo objects in a single Opn file. If return_combined_outlines = FALSE, returns a list of coordinate matrices of each open outline. 14 | #' 15 | #' @export 16 | 17 | 18 | 19 | open_outlines_from_closed_outlines <- function(outlines_combined, return_combined_outlines = TRUE) { 20 | open_outlines_list <- list() 21 | 22 | 23 | dist_to_start_and_end <- function(x,y, max_y, min_y){ 24 | 25 | DistToStart <- ((0 - x)^2 + (max_y - y)^2)^0.5 26 | current_outline_df[current_row, "DistToStart"] <- DistToStart 27 | 28 | DistToEnd <- ((0 - x)^2 + (0 - y)^2)^0.5 29 | current_outline_df[current_row, "DistToEnd"] <- DistToEnd 30 | 31 | return(current_outline_df[current_row, ]) 32 | 33 | } 34 | 35 | pb <- utils::txtProgressBar(min = 0, max = length(outlines_combined), style = 3) 36 | for (counter in 1:length(outlines_combined)) { 37 | current_outline_name <- names(outlines_combined$coo[counter]) 38 | 39 | current_outline <- outlines_combined$coo[[counter]] 40 | 41 | current_outline_df <- as.data.frame(current_outline) 42 | names(current_outline_df) <- c("X", "Y") 43 | 44 | current_outline_df$DistToStart <- NA 45 | current_outline_df$DistToEnd <- NA 46 | 47 | test_list <- list() 48 | max_y <- max(current_outline_df$Y)#+100 49 | # min_y <- max(current_outline_df$Y)*0.3 50 | for (current_row in 1:nrow(current_outline_df)) { 51 | so <- current_outline_df[current_row,] 52 | test_list[[current_row]] <- dist_to_start_and_end(x = so$X, y = so$Y, max_y = max_y) 53 | } 54 | result_df <- do.call("rbind", test_list) 55 | 56 | # plot(result_df[,c("X","Y")], cex = 0.5) 57 | # points(result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")], col = "blue", cex = 5, add = T) 58 | # points(result_df[which(result_df$DistToEnd == min(result_df$DistToEnd)),c("X","Y")], col = "red", cex = 5, add = T) 59 | 60 | min_x_current <- result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")]$X 61 | subset_min_x_y <- subset(result_df, X == min_x_current) 62 | 63 | # points(subset_min_x_y[which(subset_min_x_y$Y == min(subset_min_x_y$Y)),c("X","Y")], col = "green", cex = 5, add = T) 64 | 65 | 66 | start <- as.integer(rownames(result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")])) 67 | end <- as.integer(rownames(subset_min_x_y[which(subset_min_x_y$Y == min(subset_min_x_y$Y)),c("X","Y")])) 68 | 69 | closed <- Momocs::Opn(current_outline[c(1:end, start:nrow(current_outline)), ]) 70 | 71 | open_outlines_list[[current_outline_name]] <- Momocs::coo_slidegap(closed, force = T) 72 | 73 | 74 | setTxtProgressBar(pb, counter) 75 | } 76 | close(pb) 77 | 78 | 79 | if (return_combined_outlines == TRUE) { 80 | return(combine_outlines(open_outlines_list)) 81 | } else { 82 | return(open_outlines_list) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /R/separate_single_artefacts_function.R: -------------------------------------------------------------------------------- 1 | #' separate_single_artefacts 2 | #' 3 | #' A function to separate single artefacts from an image containing 4 | #' multiple artefacts. 5 | #' 6 | #' @param inpath Pathname to the folder containing the JPEG 7 | #' images with multiple artefacts on it. 8 | #' @param outpath Pathname to the folder where the JPEGs of 9 | #' the singled-out artefacts from this function should be stored. 10 | #' 11 | #' @return If return_combined_outlines = TRUE, returns the combined 12 | #' Coo objects in a single Opn file. If return_combined_outlines = FALSE, 13 | #' returns a list of coordinate matrices of each open outline. 14 | #' 15 | #' @export 16 | separate_single_artefacts <- function(inpath, outpath) { 17 | 18 | files_to_use_names <- list.files(inpath, 19 | full.names = FALSE, 20 | pattern = c(".jpg", ".jpeg", ".JPG", ".JPEG")) 21 | inpath <- list.files(inpath, 22 | full.names = TRUE, 23 | pattern = c(".jpg", ".jpeg", ".JPG", ".JPEG")) 24 | 25 | pb <- utils::txtProgressBar(min = 0, max = length(inpath), style = 3) 26 | for (current_masked_file in 1:length(inpath)) { 27 | x_raw <- imager::load.image(inpath[current_masked_file]) 28 | 29 | x_grayscale <- imager::grayscale(x_raw) 30 | 31 | # because some images had to be pre-processed in GIMP with the thresholding tool, this step might result in errors 32 | x_threshold <- x_grayscale 33 | try(x_threshold <- imager::threshold(x_threshold), 34 | silent = TRUE) 35 | x_clean <- imager::clean(x_threshold, 10) 36 | x_clean_cimg <- imager::as.cimg(x_clean) 37 | 38 | x_clean_img <- EBImage::Image(x_clean_cimg) 39 | x_clean_img_filled <- EBImage::fillHull(1 - x_clean_img) 40 | 41 | bin_image <- round(x_clean_img_filled / max(x_clean_img_filled), 0) 42 | 43 | bin_image_labeled <- EBImage::bwlabel(bin_image) 44 | 45 | # important step to be able to work with images that are already binary and images that are still in grayscale 46 | if (EBImage::numberOfFrames(bin_image_labeled) == 1) { 47 | bin_image_labeled_filled_frame <- try(EBImage::getFrame(bin_image_labeled, 1), 48 | silent = TRUE 49 | ) 50 | } else { 51 | bin_image_labeled_filled_frame <- try(EBImage::getFrame(bin_image_labeled, 3), 52 | silent = TRUE 53 | ) 54 | } 55 | 56 | features <- EBImage::computeFeatures.shape(bin_image_labeled_filled_frame) 57 | 58 | all_objects <- 1:max(bin_image_labeled_filled_frame) 59 | 60 | for (object_counter in all_objects) { 61 | 62 | # single out individual artefacts (starting with the largest) 63 | current_object <- EBImage::rmObjects( 64 | bin_image_labeled_filled_frame, 65 | all_objects[all_objects != object_counter] 66 | ) 67 | 68 | # inverts the image (black artefact on white background) 69 | current_object_inverted <- 1 - current_object 70 | 71 | # save it as .jpg with a pseudo-number (does not represent the number which the artefact might have on the page) 72 | EBImage::writeImage( 73 | current_object_inverted, 74 | file.path(outpath, 75 | paste0(strsplit(files_to_use_names[current_masked_file], split = "[.]")[[1]][1], 76 | "_pseudo_no_", 77 | object_counter, 78 | ".jpg")) 79 | ) 80 | } 81 | 82 | gc() 83 | utils::setTxtProgressBar(pb, current_masked_file) 84 | } 85 | close(pb) 86 | } 87 | -------------------------------------------------------------------------------- /R/tps_to_dataframe_function.R: -------------------------------------------------------------------------------- 1 | #' tps_to_df 2 | #' 3 | #' Regular Expression loop to get .tps files containing "LM", "IMAGE", "ID", and "SCALE" information (no more no less) into a dataframe, 4 | #' which subsequently can be used in the function *get_outlines()* for scaling. 5 | #' 6 | #' @param tps_file_path The path to the .tps file containing "LM", "IMAGE", "ID", and "SCALE" information (no more no less), as for example created using tpsUtil and tpsDig2 (Rohlf 2017). 7 | #' 8 | #' @return Returns a dataframe with "LM", "IMAGE", "ID", and "SCALE" as column names. 9 | #' 10 | #' @export 11 | 12 | 13 | 14 | tps_to_df <- function(tps_file_path = tps_file_path) { 15 | tps_file <- readChar(tps_file_path, 16 | file.info(tps_file_path)$size) 17 | 18 | split_by_image <- strsplit(tps_file, split = "IMAGE=")[[1]] 19 | split_by_image <- split_by_image[2:length(split_by_image)] 20 | 21 | df_list <- list() 22 | for(current_img_index in 1:length(split_by_image)){ 23 | 24 | current_scale <- if(is.na(stringr::str_extract(pattern = "SCALE=", string = split_by_image[current_img_index])) == FALSE){ 25 | gsub(".*SCALE=|\r\n.*", "", split_by_image[current_img_index]) 26 | } else{"NA"} 27 | 28 | df_list[[current_img_index]] <- 29 | data.frame(IMAGE = strsplit(gsub(".*.^|\r\n.*", "", split_by_image[current_img_index]), split = "\\.")[[1]][1], 30 | ID = gsub(".*ID=|\r\n.*", "", split_by_image[current_img_index]), 31 | SCALE = current_scale) 32 | 33 | } 34 | tps_file_df <- do.call(rbind.data.frame, df_list) 35 | 36 | tps_file_df$SCALE <- as.numeric(gsub(",", ".", x = tps_file_df$SCALE)) 37 | 38 | return(tps_file_df) 39 | } 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # outlineR: An R package to derive outline shapes from (multiple) artefacts on JPEG images 2 | [![DOI](https://img.shields.io/badge/DOI-10.5281/zenodo.4527469-blue)](https://doi.org/10.5281/zenodo.4527469) 3 | 4 | This package is a helpful wrapper around functions from mainly the __Momocs__ (Bonhomme et al. 2014), __EBImage__ (Pau et al. 2010), and __imager__ (Barthelme et al. 2020) packages. It is designed for the fast and easy extraction of single outline shapes of, for example, stone tools from images containing multiple thereof, such as the ones present in archaeological publications. 5 | 6 | An in-depth, __step-by-step protocol__ can be found on https://dx.doi.org/10.17504/protocols.io.bygaptse. 7 | 8 | ## Installation 9 | 10 | ``` 11 | remotes::install_github("yesdavid/outlineR") 12 | ``` 13 | 14 | ## Workflow 15 | 16 | ### Raw data 17 | 18 | The raw data should have a sufficiently high resolution. 19 | 20 | 21 | 22 | 31 | 32 |
23 |
24 | 25 |
26 | Raw data (here: Morar Quartz Industry) as it can be found in archaeological publications. Credit: 27 | Wellcome Collection (CC BY 4.0). 28 |
29 |
30 |
33 | 34 | ### Manual image preparation 35 | 36 | The raw images have to be converted to JPEG format and manually prepared in an image manipulation software. Numberings, descriptions, etc. (everything we do not want an outline of) have to be removed by hand. If the image is a picture taken of the artefacts, make sure to remove any cast shadows. 37 | 38 | With the images cleaned, they now have to be thresholed/binarized[^1]. If not, the binarization happens inside the outlineR-package. However, it is not very robust and I advise you to do it manually beforehand. 39 | 40 | [^1]: In GIMP2, images can be thresholded/biarized under "_Colors_" -> "_Thresholding..._" 41 | 42 | 43 | 44 | 52 | 53 |
45 |
46 | 47 |
48 | Manually prepared JPEG image with numberings etc. removed and thresholded using GIMP. It is suited to be used as input file for the `separate_single_artefacts` function and should therefore be saved in the `inpath`-folder (see below). 49 |
50 |
51 |
54 | 55 | ### outlineR application 56 | 57 | #### 0. Load the package 58 | 59 | ``` 60 | library(outlineR) 61 | ``` 62 | 63 | #### 1. Path settings 64 | 65 | We define `inpath` as the pathname to out manually prepared image (see above). `outpath` defines the path to an empty folder where all prepared and singled-out artefacts should be saved to. 66 | ``` 67 | # For example: 68 | # Define where the images containing multiple artefacts are right now. 69 | inpath <- "./test_data/input_data" 70 | 71 | # Define where the separate images should be saved. 72 | outpath <- "./test_data/derived_data" 73 | ``` 74 | 75 | #### 2. Separation of single artefacts 76 | 77 | `separate_single_artefacts` separates single artefacts from pictures with multiple artefacts on it. 78 | 79 | ``` 80 | separate_single_artefacts(inpath = inpath, 81 | outpath = outpath) 82 | ``` 83 | 84 | Afterwards, the JPEGs of the single artefacts should be saved in the folder which you defined under `outpath`. If there is just plain white images in your `outpath` folder, re-check the images you prepared in `inpath` for single outlier pixels or open outlines. If necessary, delete all JPEGS in `outpath`. Then, re-run this command. 85 | 86 | 87 | 88 | 96 | 97 |
89 |
90 | 91 |
92 | The single, separated artefacts from our input file generated using `separate_single_artefacts`, now located in `outpath`. 93 |
94 |
95 |
98 | 99 | 100 | #### 3. Outline extraction 101 | 102 | Using `Momocs::import_jpg()`, this function extracts the outlines of the images, while at the same time preserving the images' names. This function only needs the files in your `outpath` folder, so you do not (necessarily) have to run all of the code above again. 103 | 104 | ``` 105 | single_outlines_list <- get_outlines(outpath = outpath, tps_file_rescale = NULL) 106 | ``` 107 | 108 | If a .tps file with scaling factor is available, it can be transformed into a dataframe using the `tps_to_df()` function and forwarded into `get_outlines()`. 109 | 110 | ``` 111 | tps_df <- tps_to_df("path/to/tps/file.tps") 112 | 113 | single_outlines_list <- get_outlines(outpath = outpath, tps_file_rescale = tps_df) 114 | ``` 115 | 116 | 117 | #### 4. Outline combination 118 | 119 | The list of single outlines is combined into a single Out/Opn (Momocs) file. 120 | ``` 121 | outlines_combined <- combine_outlines(single_outlines_list = single_outlines_list) 122 | ``` 123 | 124 | #### 5. Outline inspection 125 | 126 | In a last step before starting to work with your outlines, you should inspect the just created outlines for validity and possible errors. 127 | ``` 128 | length(outlines_combined) #how many outlines do you have? 129 | stack(outlines_combined) # shows all outlines above one another(you might want to center and scale them first using Momocs) 130 | Momocs::panel(outlines_combined) # shows all outlines next to each other 131 | Momocs::inspect(outlines_combined) # shows only a single outline at a time. 132 | ``` 133 | 134 | 135 | 136 | 145 | 153 | 154 |
137 |
138 | 139 |
140 | Stacked outlines. 141 |
142 |
143 | 144 |
146 |
147 | 148 |
149 | Panel of outlines. 150 |
151 |
152 |
155 | 156 | ## References 157 | 158 | __Barthelme et al. 2020__: Barthelme, S., Tschumperle, D., Wijffels, J., Assemlal, H. E., & Ochi, S. (2020). imager: Image Processing Library Based on “CImg” (0.42.3) [Computer software]. https://CRAN.R-project.org/package=imager 159 | 160 | __Bonhomme et al. 2014__: Bonhomme, V., Picq, S., Gaucherel, C., & Claude, J. (2014). Momocs: Outline Analysis Using R. Journal of Statistical Software, 56(13). https://doi.org/10.18637/jss.v056.i13 161 | 162 | __Pau et al. 2010__: Pau, G., Fuchs, F., Sklyar, O., Boutros, M., & Huber, W. (2010). EBImage—An R package for image processing with applications to cellular phenotypes. Bioinformatics, 26(7), 979–981. https://doi.org/10.1093/bioinformatics/btq046 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /man/combine_outlines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/combine_outlines_function.R 3 | \name{combine_outlines} 4 | \alias{combine_outlines} 5 | \title{combine_outlines} 6 | \usage{ 7 | combine_outlines(single_outlines_list) 8 | } 9 | \arguments{ 10 | \item{single_outlines_list}{A list containing separate coordinate 11 | matrices (Momocs' Coo objects) as for example created with the 12 | separate_single_artefacts function.} 13 | } 14 | \value{ 15 | Returns the combined Coo objects in a single Out/Opn file 16 | } 17 | \description{ 18 | A function containing a recursive loop to combine a list of single 19 | outline coordinate-sets into a single Momocs::Out/Opn file 20 | } 21 | -------------------------------------------------------------------------------- /man/get_outlines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_outlines_function.R 3 | \name{get_outlines} 4 | \alias{get_outlines} 5 | \title{get_outlines} 6 | \usage{ 7 | get_outlines(outpath, tps_file_rescale = NULL) 8 | } 9 | \arguments{ 10 | \item{outpath}{The path to the folder containing the .jpg files of the singled out artefacts 11 | from which the outlines should be derived (as for example created in the 12 | separate_single_artefacts function).} 13 | 14 | \item{tps_file_rescale}{(Default = NULL) A dataframe containing at least a column \emph{IMAGE} and a column \emph{SCALE}. 15 | This dataframe is internally created using the function \emph{tps_to_df} (for more information see ?tps_to_df()). 16 | \emph{IMAGE} contains the names of the images, as they are written in \emph{inpath}. 17 | \emph{SCALE} contains the scaling factor from pixel to a metric measurement. 18 | Such a file can be created in i.e. tpsDIG2 (Rohlf 2017).} 19 | } 20 | \value{ 21 | Returns a Momocs Out file containing the outlines' coordinates 22 | with their associated IDs, derived from the file name. 23 | } 24 | \description{ 25 | Uses Momocs::import_jpg() to import outline coordinates from the single 26 | .jpg files which are either already avaiable, or were created using the 27 | separate_single_artefacts function. This function preserves the 28 | filenames. 29 | } 30 | -------------------------------------------------------------------------------- /man/open_outlines_from_closed_outlines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/open_outlines_from_closed_outlines_v3.R 3 | \name{open_outlines_from_closed_outlines} 4 | \alias{open_outlines_from_closed_outlines} 5 | \title{open_outlines_from_closed_outlines} 6 | \usage{ 7 | open_outlines_from_closed_outlines( 8 | outlines_combined, 9 | return_combined_outlines = TRUE 10 | ) 11 | } 12 | \arguments{ 13 | \item{outlines_combined}{A Momocs Out file containing closed outlines.} 14 | 15 | \item{return_combined_outlines}{(default = TRUE) A logical parameter stating wether to output a Momocs Opn-file, or a list of coordinate matrices of each open outline.} 16 | } 17 | \value{ 18 | If return_combined_outlines = TRUE, returns the combined Coo objects in a single Opn file. If return_combined_outlines = FALSE, returns a list of coordinate matrices of each open outline. 19 | } 20 | \description{ 21 | A function to create open outlines from closed outlines. 22 | The way it is implemented, the open outline will start at the highest, left-most coordinate, 23 | run clock-wise, and will end at the lowest, left-most coordinate. 24 | The starting point is found using the shortest distance from an arificial point on X = 0 and Y = max(current_outline_df$Y) (i.e., the overall highest Y-coordinate), 25 | to the artefacts outline. The ending point is found using the starting points X-coordinate and it's corresponding minimum Y-coordinate. 26 | } 27 | \details{ 28 | This works only for artefacts, which have their supposed open side on their left and have been prepared in a way, so that this open left side is a straigt vertical line. 29 | } 30 | -------------------------------------------------------------------------------- /man/separate_single_artefacts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/separate_single_artefacts_function.R 3 | \name{separate_single_artefacts} 4 | \alias{separate_single_artefacts} 5 | \title{separate_single_artefacts} 6 | \usage{ 7 | separate_single_artefacts(inpath, outpath) 8 | } 9 | \arguments{ 10 | \item{inpath}{Pathname to the folder containing the JPEG 11 | images with multiple artefacts on it.} 12 | 13 | \item{outpath}{Pathname to the folder where the JPEGs of 14 | the singled-out artefacts from this function should be stored.} 15 | } 16 | \value{ 17 | If return_combined_outlines = TRUE, returns the combined 18 | Coo objects in a single Opn file. If return_combined_outlines = FALSE, 19 | returns a list of coordinate matrices of each open outline. 20 | } 21 | \description{ 22 | A function to separate single artefacts from an image containing 23 | multiple artefacts. 24 | } 25 | -------------------------------------------------------------------------------- /man/tps_to_df.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tps_to_dataframe_function.R 3 | \name{tps_to_df} 4 | \alias{tps_to_df} 5 | \title{tps_to_df} 6 | \usage{ 7 | tps_to_df(tps_file_path = tps_file_path) 8 | } 9 | \arguments{ 10 | \item{tps_file_path}{The path to the .tps file containing "LM", "IMAGE", "ID", and "SCALE" information (no more no less), as for example created using tpsUtil and tpsDig2 (Rohlf 2017).} 11 | } 12 | \value{ 13 | Returns a dataframe with "LM", "IMAGE", "ID", and "SCALE" as column names. 14 | } 15 | \description{ 16 | Regular Expression loop to get .tps files containing "LM", "IMAGE", "ID", and "SCALE" information (no more no less) into a dataframe, 17 | which subsequently can be used in the function \emph{get_outlines()} for scaling. 18 | } 19 | -------------------------------------------------------------------------------- /outlineR.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | LineEndingConversion: Posix 18 | 19 | BuildType: Package 20 | PackageUseDevtools: Yes 21 | PackageInstallArgs: --no-multiarch --with-keep.source 22 | PackageRoxygenize: rd,collate,namespace 23 | -------------------------------------------------------------------------------- /playground/open_outlines_from_closed_outlines.R: -------------------------------------------------------------------------------- 1 | #' open_outlines_from_closed_outlines 2 | #' 3 | #' A function to create open outlines from closed outlines. The way it is implemented, the open outline will start at the highest, left-most coordinate, run clock-wise, and will end at the lowest, left-most coordinate. 4 | #' 5 | #' @param outlines_combined A Momocs Out file containing closed outlines. 6 | #' @param return_combined_outlines (default = TRUE) A logical parameter stating wether to output a Momocs Opn-file, or a list of coordinate matrices of each open outline. 7 | #' @return If return_combined_outlines = TRUE, returns the combined Coo objects in a single Opn file. If return_combined_outlines = FALSE, returns a list of coordinate matrices of each open outline. 8 | #' 9 | #' @export 10 | 11 | 12 | 13 | open_outlines_from_closed_outlines <- function(outlines_combined, return_combined_outlines = TRUE) { 14 | open_outlines_list <- list() 15 | 16 | pb <- utils::txtProgressBar(min = 0, max = length(outlines_combined), style = 3) 17 | for (counter in 1:length(outlines_combined)) { 18 | current_outline_name <- names(outlines_combined$coo[counter]) 19 | 20 | current_outline <- outlines_combined$coo[[counter]] 21 | 22 | current_outline_df <- as.data.frame(current_outline) 23 | names(current_outline_df) <- c("X", "Y") 24 | 25 | df_starting_lowest_x_highest_y <- current_outline_df[!duplicated(current_outline_df$X) & max(current_outline_df$Y), ] 26 | df_starting_lowest_x_highest_y <- current_outline_df[!duplicated(df_starting_lowest_x_highest_y$Y), ] 27 | 28 | # starting_lowest_x <- subset(df_starting_lowest_x_highest_y, 29 | # X == min(df_starting_lowest_x_highest_y$X)) 30 | # starting_lowest_x_highest_y <- subset(starting_lowest_x, 31 | # Y == max(starting_lowest_x$Y)) 32 | 33 | optim_starting <- df_starting_lowest_x_highest_y[with(df_starting_lowest_x_highest_y, order(X, -Y)), ][1, ] 34 | 35 | 36 | df_ending_lowest_x_lowest_y <- current_outline_df[!duplicated(current_outline_df$X) & min(current_outline_df$Y), ] 37 | df_ending_lowest_x_lowest_y <- current_outline_df[!duplicated(df_ending_lowest_x_lowest_y$Y), ] 38 | # 39 | # ending_lowest_x <- subset(df_starting_lowest_x_highest_y, 40 | # X == min(current_outline_df$X)) 41 | # ending_lowest_x_lowest_y <- subset(ending_lowest_x, 42 | # Y == min(ending_lowest_x$Y)) 43 | optim_ending <- df_ending_lowest_x_lowest_y[with(df_ending_lowest_x_lowest_y, order(X, Y)), ][1, ] 44 | 45 | start <- rownames(starting_lowest_x_highest_y) 46 | end <- rownames(ending_lowest_x_lowest_y) 47 | 48 | closed <- Momocs::Opn(current_outline[c(1:end, start:nrow(current_outline)), ]) 49 | 50 | open_outlines_list[[current_outline_name]] <- coo_slidegap(closed, force = T) 51 | 52 | 53 | setTxtProgressBar(pb, counter) 54 | } 55 | close(pb) 56 | 57 | 58 | if (return_combined_outlines == TRUE) { 59 | return(combine_outlines(open_outlines_list)) 60 | } else { 61 | return(open_outlines_list) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /playground/open_outlines_from_closed_outlines_v2.R: -------------------------------------------------------------------------------- 1 | #' open_outlines_from_closed_outlines 2 | #' 3 | #' A function to create open outlines from closed outlines. The way it is implemented, the open outline will start at the highest, left-most coordinate, run clock-wise, and will end at the lowest, left-most coordinate. 4 | #' 5 | #' @param outlines_combined A Momocs Out file containing closed outlines. 6 | #' @param return_combined_outlines (default = TRUE) A logical parameter stating wether to output a Momocs Opn-file, or a list of coordinate matrices of each open outline. 7 | #' @return If return_combined_outlines = TRUE, returns the combined Coo objects in a single Opn file. If return_combined_outlines = FALSE, returns a list of coordinate matrices of each open outline. 8 | #' 9 | #' @export 10 | 11 | 12 | 13 | open_outlines_from_closed_outlines <- function(outlines_combined, return_combined_outlines = TRUE) { 14 | open_outlines_list <- list() 15 | 16 | 17 | dist_to_start_and_end <- function(x,y, max_y){ 18 | 19 | DistToStart <- ((0 - x)^2 + (max_y - y)^2)^0.5 20 | current_outline_df[current_row, "DistToStart"] <- DistToStart 21 | 22 | DistToEnd <- ((0 - x)^2 + (0 - y)^2)^0.5 23 | current_outline_df[current_row, "DistToEnd"] <- DistToEnd 24 | 25 | return(current_outline_df[current_row, ]) 26 | 27 | } 28 | 29 | pb <- utils::txtProgressBar(min = 0, max = length(outlines_combined), style = 3) 30 | for (counter in 1:length(outlines_combined)) { 31 | current_outline_name <- names(outlines_combined$coo[counter]) 32 | 33 | current_outline <- outlines_combined$coo[[counter]] 34 | 35 | current_outline_df <- as.data.frame(current_outline) 36 | names(current_outline_df) <- c("X", "Y") 37 | 38 | current_outline_df$DistToStart <- NA 39 | current_outline_df$DistToEnd <- NA 40 | 41 | test_list <- list() 42 | max_y <- max(current_outline_df$Y)+100 43 | for (current_row in 1:nrow(current_outline_df)) { 44 | so <- current_outline_df[current_row,] 45 | test_list[[current_row]] <- dist_to_start_and_end(x = so$X, y = so$Y, max_y = max_y) 46 | } 47 | result_df <- do.call("rbind", test_list) 48 | 49 | # plot(result_df[,c("X","Y")], cex = 0.5) 50 | # points(result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")], col = "blue", cex = 5, add = T) 51 | # points(result_df[which(result_df$DistToEnd == min(result_df$DistToEnd)),c("X","Y")], col = "red", cex = 5, add = T) 52 | 53 | 54 | 55 | start <- as.integer(rownames(result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")])) 56 | end <- as.integer(rownames(result_df[which(result_df$DistToEnd == min(result_df$DistToEnd)),c("X","Y")])) 57 | 58 | closed <- Momocs::Opn(current_outline[c(1:end, start:nrow(current_outline)), ]) 59 | 60 | open_outlines_list[[current_outline_name]] <- Momocs::coo_slidegap(closed, force = T) 61 | 62 | 63 | setTxtProgressBar(pb, counter) 64 | } 65 | close(pb) 66 | 67 | 68 | if (return_combined_outlines == TRUE) { 69 | return(combine_outlines(open_outlines_list)) 70 | } else { 71 | return(open_outlines_list) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /playground/open_outlines_from_closed_outlines_v3.R: -------------------------------------------------------------------------------- 1 | #' open_outlines_from_closed_outlines 2 | #' 3 | #' A function to create open outlines from closed outlines. The way it is implemented, the open outline will start at the highest, left-most coordinate, run clock-wise, and will end at the lowest, left-most coordinate. 4 | #' 5 | #' @param outlines_combined A Momocs Out file containing closed outlines. 6 | #' @param return_combined_outlines (default = TRUE) A logical parameter stating wether to output a Momocs Opn-file, or a list of coordinate matrices of each open outline. 7 | #' @return If return_combined_outlines = TRUE, returns the combined Coo objects in a single Opn file. If return_combined_outlines = FALSE, returns a list of coordinate matrices of each open outline. 8 | #' 9 | #' @export 10 | 11 | 12 | 13 | open_outlines_from_closed_outlines <- function(outlines_combined, return_combined_outlines = TRUE) { 14 | open_outlines_list <- list() 15 | 16 | 17 | dist_to_start_and_end <- function(x,y, max_y, min_y){ 18 | 19 | DistToStart <- ((0 - x)^2 + (max_y - y)^2)^0.5 20 | current_outline_df[current_row, "DistToStart"] <- DistToStart 21 | 22 | DistToEnd <- ((0 - x)^2 + (0 - y)^2)^0.5 23 | current_outline_df[current_row, "DistToEnd"] <- DistToEnd 24 | 25 | return(current_outline_df[current_row, ]) 26 | 27 | } 28 | 29 | pb <- utils::txtProgressBar(min = 0, max = length(outlines_combined), style = 3) 30 | for (counter in 1:length(outlines_combined)) { 31 | current_outline_name <- names(outlines_combined$coo[counter]) 32 | 33 | current_outline <- outlines_combined$coo[[counter]] 34 | 35 | current_outline_df <- as.data.frame(current_outline) 36 | names(current_outline_df) <- c("X", "Y") 37 | 38 | current_outline_df$DistToStart <- NA 39 | current_outline_df$DistToEnd <- NA 40 | 41 | test_list <- list() 42 | max_y <- max(current_outline_df$Y)#+100 43 | # min_y <- max(current_outline_df$Y)*0.3 44 | for (current_row in 1:nrow(current_outline_df)) { 45 | so <- current_outline_df[current_row,] 46 | test_list[[current_row]] <- dist_to_start_and_end(x = so$X, y = so$Y, max_y = max_y) 47 | } 48 | result_df <- do.call("rbind", test_list) 49 | 50 | # plot(result_df[,c("X","Y")], cex = 0.5) 51 | # points(result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")], col = "blue", cex = 5, add = T) 52 | # points(result_df[which(result_df$DistToEnd == min(result_df$DistToEnd)),c("X","Y")], col = "red", cex = 5, add = T) 53 | 54 | min_x_current <- result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")]$X 55 | subset_min_x_y <- subset(result_df, X == min_x_current) 56 | 57 | # points(subset_min_x_y[which(subset_min_x_y$Y == min(subset_min_x_y$Y)),c("X","Y")], col = "green", cex = 5, add = T) 58 | 59 | 60 | start <- as.integer(rownames(result_df[which(result_df$DistToStart == min(result_df$DistToStart)),c("X","Y")])) 61 | end <- as.integer(rownames(subset_min_x_y[which(subset_min_x_y$Y == min(subset_min_x_y$Y)),c("X","Y")])) 62 | 63 | closed <- Momocs::Opn(current_outline[c(1:end, start:nrow(current_outline)), ]) 64 | 65 | open_outlines_list[[current_outline_name]] <- Momocs::coo_slidegap(closed, force = T) 66 | 67 | 68 | setTxtProgressBar(pb, counter) 69 | } 70 | close(pb) 71 | 72 | 73 | if (return_combined_outlines == TRUE) { 74 | return(combine_outlines(open_outlines_list)) 75 | } else { 76 | return(open_outlines_list) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test_data/README.md: -------------------------------------------------------------------------------- 1 | 2 | # ./raw_data 3 | 4 | **Morar_Quartz_Industry_Wellcome_Collection.jpeg** 5 | 6 | Morar_Quartz_Industry_Wellcome_Collection.jpeg is a raw example image (here: Morar Quartz Industry) as it can be found in many archaeological publications. Credit: 7 | Wellcome Collection (https://wellcomecollection.org/works/th7egtfj, distributed under CC BY 4.0). 8 | 9 | 10 | # ./input_data 11 | 12 | **Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg** 13 | 14 | Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg is the raw "./raw_data/Morar_Quartz_Industry_Wellcome_Collection.jpeg" image (distributed by [Wellcome Collection](https://wellcomecollection.org/works/th7egtfj) under CC BY 4.0) manually prepared in an image manipulation software. Numberings, descriptions, etc. (everything that is not an artefact) were removed by hand. The image was thresholed/binarized in GIMP2 under "_Colors_" -> "_Thresholding..._". 15 | 16 | 17 | **tps_file.tps** 18 | 19 | tps_file.tps is a .tps file containing "LM", "IMAGE", "ID", and "SCALE" information (no more no less) for the "./input_data/Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg" image (originally distributed by [Wellcome Collection](https://wellcomecollection.org/works/th7egtfj) under CC BY 4.0) and was created using tpsUtil and tpsDig2 ([Rohlf 2017](http://www.sbmorphometrics.org/)). It can be used to scale the outlines accordingly. 20 | 21 | 22 | 23 | 24 | # ./derived_data 25 | 26 | This folder contains the singled out artefacts from the "./input_data/Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg" image (originally distributed by [Wellcome Collection](https://wellcomecollection.org/works/th7egtfj) under CC BY 4.0). These artefacts were singled out using the outlineR::separate_single_artefacts() function and can be fed into the outlineR::get_outlines() function. 27 | -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_1.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_10.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_11.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_12.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_13.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_14.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_15.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_16.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_17.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_18.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_19.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_2.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_20.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_21.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_22.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_23.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_24.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_25.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_26.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_27.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_28.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_3.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_4.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_5.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_6.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_7.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_8.jpg -------------------------------------------------------------------------------- /test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/derived_data/Morar_Quartz_Industry_Wellcome_Collection_pseudo_no_9.jpg -------------------------------------------------------------------------------- /test_data/input_data/Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/input_data/Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg -------------------------------------------------------------------------------- /test_data/input_data/tps_file.tps: -------------------------------------------------------------------------------- 1 | LM=0 2 | IMAGE=Morar_Quartz_Industry_Wellcome_Collection_clean.jpeg 3 | ID=0 4 | SCALE=0.016069 5 | -------------------------------------------------------------------------------- /test_data/panel_outlines_combined.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/panel_outlines_combined.jpeg -------------------------------------------------------------------------------- /test_data/raw_data/Morar_Quartz_Industry_Wellcome_Collection.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/raw_data/Morar_Quartz_Industry_Wellcome_Collection.jpeg -------------------------------------------------------------------------------- /test_data/screenshot_derived_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/screenshot_derived_data.png -------------------------------------------------------------------------------- /test_data/stack_outlines_combined.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yesdavid/outlineR/ab9b6b034649a784db39b230dcf095a063aa3496/test_data/stack_outlines_combined.jpeg --------------------------------------------------------------------------------