Compute the Congruent Matching Profile Segments (CMPS) score based on two bullet profiles/signatures. The reference profile will be divided into consecutive, non-overlapping, basis segments of the same length. Then the number of segments that are congruent matching will be found as the CMPS score. By default, extract_feature_cmps implements the algorithm with multi-peak inspection at three different segment scale levels. By setting npeaks_set as a single-length vector, users can switch to the algorithm with multi-peak inspection at the basis scale level only.

extract_feature_cmps(
  x,
  y,
  seg_length = 50,
  Tx = 25,
  npeaks_set = c(5, 3, 1),
  include = NULL,
  outlength = NULL
)

Arguments

x

a numeric vector, vector of the reference bullet signature/profile that will be divided into basis segments

y

a numeric vector, vector of the comparison bullet signature/profile

seg_length

a positive integer, the length of a basis segment

Tx

a positive integer, the tolerance zone is +/- Tx

npeaks_set

a numeric vector, specify the number of peaks to be found at each segment scale level

  • If length(npeaks_set) == 1, the algorithm uses multi-peak inspection only at the basis scale level;

  • If length(npeaks_set) > 1, the algorithm uses multi-peak inspection at different segment scale levels.

  • By default, npeaks_set = c(5,3,1). Including more segment scale levels will reduce the number of false positive results

include

NULL or a vector of character strings indicating what additional information should be included in the output of extract_feature_cmps. All possible options are: "nseg", "congruent_pos", "congruent_seg", "congruent_seg_idx", "pos_df", "ccp_list","segments", and "parameters". If one wants to include them all, one can use include = "full_result". By default, include = NULL and only the CMPS score is returned

outlength

NULL or a numeric vector, specify the segment length of each level of the basis segment when the multi-segment lengths strategy is being used. If outlength = NULL, then the length of a basis segment will be doubled at each segment level

Value

a numeric value or a list

  • if include = NULL, returns the CMPS score (a numeric value) only

  • if include = one or a vector of strings listed above:

    • nseg: number of basis segments

    • congruent_seg: a vector of boolean values. TRUE means this basis segment is a congruent matching profile segment (CMPS)

    • congruent_seg_idx: the indices of all CMPS

    • pos_df: a dataframe that includes positions of correlation peaks and the CMPS score of these positions

    • ccp_list: a list of consistent correlation peaks of each basis segment.

    • segments: a list of all basis segments

    • parameters: a list that stores all parameters used in the function call

References

Chen, Zhe, Wei Chu, Johannes A Soons, Robert M Thompson, John Song, and Xuezeng Zhao. 2019. “Fired Bullet Signature Correlation Using the Congruent Matching Profile Segments (CMPS) Method.” Forensic Science International, December, #109964. https://doi.org/10.1016/j.forsciint.2019.109964.

Examples

library(tidyverse)
library(cmpsR)

data("bullets")
land2_3 <- bullets$sigs[bullets$bulletland == "2-3"][[1]]
land1_2 <- bullets$sigs[bullets$bulletland == "1-2"][[1]]

# compute cmps

# algorithm with multi-peak insepction at three different segment scale levels
cmps_with_multi_scale <- extract_feature_cmps(land2_3$sig, land1_2$sig, include = "full_result" )

# algorithm with multi-peak inspection at the basis scale level only
cmps_without_multi_scale <- extract_feature_cmps(land2_3$sig, land1_2$sig, 
                                                 npeaks_set = 5, include = "full_result" )

# Another example
library(tidyverse)

data("bullets")

lands <- unique(bullets$bulletland)

comparisons <- data.frame(expand.grid(land1 = lands[1:6], land2 = lands[7:12]),
                          stringsAsFactors = FALSE)

comparisons <- comparisons %>%
  left_join(bullets %>% select(bulletland, sig1=sigs),
            by = c("land1" = "bulletland")) %>%
  left_join(bullets %>% select(bulletland, sig2=sigs),
            by = c("land2" = "bulletland"))

comparisons <- comparisons %>% mutate(
  cmps = purrr::map2(sig1, sig2, .f = function(x, y) {
    extract_feature_cmps(x$sig, y$sig, include = "full_result")
  })
)

comparisons <- comparisons %>%
  mutate(
    cmps_score = sapply(comparisons$cmps, function(x) x$CMPS_score),
    cmps_nseg = sapply(comparisons$cmps, function(x) x$nseg)
  )
  
cp1 <- comparisons %>% select(land1, land2, cmps_score, cmps_nseg)
cp1  
#>    land1 land2 cmps_score cmps_nseg
#> 1    1-1   2-1          2        23
#> 2    1-2   2-1          2        22
#> 3    1-3   2-1          1        21
#> 4    1-4   2-1          2        22
#> 5    1-5   2-1          2        23
#> 6    1-6   2-1         16        22
#> 7    1-1   2-2          3        23
#> 8    1-2   2-2          1        22
#> 9    1-3   2-2          1        21
#> 10   1-4   2-2          1        22
#> 11   1-5   2-2          2        23
#> 12   1-6   2-2          3        22
#> 13   1-1   2-3          2        23
#> 14   1-2   2-3         17        22
#> 15   1-3   2-3          3        21
#> 16   1-4   2-3          1        22
#> 17   1-5   2-3          1        23
#> 18   1-6   2-3          1        22
#> 19   1-1   2-4          2        23
#> 20   1-2   2-4          1        22
#> 21   1-3   2-4         14        21
#> 22   1-4   2-4          1        22
#> 23   1-5   2-4          1        23
#> 24   1-6   2-4          2        22
#> 25   1-1   2-5          1        23
#> 26   1-2   2-5          2        22
#> 27   1-3   2-5          1        21
#> 28   1-4   2-5         10        22
#> 29   1-5   2-5          1        23
#> 30   1-6   2-5          1        22
#> 31   1-1   2-6          2        23
#> 32   1-2   2-6          3        22
#> 33   1-3   2-6          1        21
#> 34   1-4   2-6          1        22
#> 35   1-5   2-6         15        23
#> 36   1-6   2-6          1        22