# Chapter 3 Firearms: bullets

#### Eric Hare, Heike Hofmann

Figure 3.1: Close-up of a bullet under a Confocal Light Microscope in the Roy J Carver High-resolution microscopy lab at Iowa State University. Photo by Heike Hofmann. Source: forensicstats.org

## 3.1 Introduction

When a bullet is fired from a gun barrel, small imperfections in the barrel leave striation marks on the bullet. These marks are expressed most in the area of the bullet that has the closest contact to the barrel.

These engravings are assumed to be unique to individual gun barrels, and as a result, traditional forensic science methods have employed trained forensic examiners to assess the likelihood of two bullets being fired from the same barrel (a “match”). Conventionally, this has been done using the metric Consecutively Matching Striae(CMS) (Biasotti 1959). However, no official standards have been established to scientifically delineate a number that effectively separates matches from non-matches. Therefore, significant work has been done, and continues to be done, in order to add scientific rigor to the bullet matching process.

The 2009 National Academy of Sciences Report (National Research Council 2009a) may have been the “call-to-arms” that the field needed. This report criticized the lack of rigor in the field at the time, but also described the “path forward”. As the authors saw it, the path forward included adoption of standards. A standard format to represent the structure of bullets opened the door for much of what you’ll read about in this chapter, including opening up the formerly unknown process of bullet matching to a much wider audience, and providing the foundations for truly automated, statistical algorithms to perform the procedure.

In this chapter, we outline the new standard data format used to store three-dimensional bullet scans. We proceed by outlying relevant R packages for the processing and analysis of these scans. Finally, we discuss ways in which to draw conclusions based on these results, and tie it all together in the form of a relevant case study.

## 3.2 Data

Data on both breech face impression and land engraved areas are available from the NIST Ballistics Toolmark Research Database (NBTRD) in the x3p (XML 3-D Surface Profile) format. The x3p format was designed to implement a standard for exchanging 3D profile data. It was adopted by the Open Forensic Metrology Consortium, or OpenFMC, a group of firearm forensics researchers whose aim is to establish best practices for researchers using metrology in forensic science.

Figure 3.2 shows an illustration of the internal structure of the x3p file format. x3p files contain an XML data file with metadata on the bullet scans, as well as binary data containing the surface topology measurements. The metadata includes information on the scanning equipment and operator, as well as information on the resolution of the scans.

Figure 3.2: An illustration of the internal structure of the x3p file format. x3p files contain an XML data file with metadata on the bullet scans, as well as binary data containing the surface topology measurements. Source: openGPS

The use of the x3p format has positively impacted procedures relating to forensic analysis of bullets. Because the format is an open standard, researchers on a wide range of computing platforms can access and analyze the data. Due to the x3p container holding a rich set of metadata, the limitations of traditional “black box”-type file formats are eliminated. The source, parameters, and raw data contained within each 3D scan is readily available for critical analysis and examination.

## 3.3 R Package(s)

The first R package created to read and process x3p files was `x3pr` (OpenFMC 2014). This package includes reading routines to read in both the data as well as the metadata of a particular bullet land. The package also has some plotting functions and a writing routine to create x3p files. A new package, `x3ptools` (Hofmann et al. 2018), was created to handle some limitations in `x3pr` and expand upon the functionality. A companion package, `bulletxtrctr` (Hofmann, Vanderplas, and Krishnan 2018), expands upon x3ptools and provides functions to perform an automated bullet analysis routine based on the algorithms described in Hare, Hofmann, and Carriquiry (2017).

The two packages `x3ptools` and `bulletxtrctr` will be the focus of the remainder of this chapter.

### 3.3.1 x3ptools

Although `x3ptools` isn’t written specifically for the purposes of handling bullet scans, it is the package of choice to begin a bullet analysis. In fact, the package itself is generic and can handle a wide range of data types that use the x3p container format.

To begin, the package can be installed from CRAN (stable release) or GitHub (development version):

    # from CRAN: install.packages('x3ptools') install development version from
    # GitHub:
    devtools::install_github("heike/x3ptools")

We load the package and use some built-in x3p data to get a feel for the package functionality. We will work with the Center for Statistical Applications in Forensic Evidence (CSAFE) logo. In its original colored form, the logo looks like Figure 3.3.

Figure 3.3: The CSAFE logo. Source: CSAFE.

A 3D version of this logo is available in `x3ptools`, where portions of the logo are raised and recessed. This makes for a good test case in introducing `x3ptools` and the idea behind 3D scans of objects, as we transition towards bullet analysis.

    library(tidyverse)
    library(x3ptools)

    logo <- read_x3p(system.file("csafe-logo.x3p", package = "x3ptools"))
    names(logo)

    ## [1] "header.info"    "surface.matrix" "feature.info"   "general.info"
    ## [5] "matrix.info"

We can see that there are five elements to the list object returned:

- header.info - Provides us information on the resolution of the scan
- surface.matrix - The actual surface data of the scan
- feature.info - Properties of the scan itself
- general.info - Information on how the data was captured
- matrix.info - Some information expanding upon header.info

The two most relevant for our purposes are header.info and surface.matrix. To begin to understand this container format better, we can use the `image_x3p` function to produce a visualization of the surface, shown in Figure 3.4.

    image_x3p(logo)

Figure 3.4: 3D surface scan of the CSAFE logo. (Rendered image has been down-sampled to speed up page load.)

We can use the function `x3p_to_df` in order to convert this structure into a standard R data frame, which will allow us to do any number of data manipulation and plotting routines. In this case, Figure 3.5 shows a simple scatter plot created with `ggplot2` of the height measurements across the surface of the bullet.

    logo_df <- x3p_to_df(logo)

    ggplot(data = logo_df, aes(x = x, y = y, color = value)) + geom_point() + scale_color_gradient(low = "white",
        high = "black") + theme_bw()

Figure 3.5: A simple scatterplot created with ggplot2 of the height measurements across the surface of the bullet.

A key feature of the data is that the `value` column represents the height of the pixel corresponding to the particular \((x,y)\) location. In this logo, we can see that the fingerprint section of the logo is raised above the background quite clearly. As we transition to operating on images of bullets, this will be important to note.

One other important feature of the package is the ability to sample. Depending on the size and resolution of a particular scan, the resulting object could be quite large. This CSAFE logo, despite being a relatively small physical size, still results in a 310,479 row data frame. Though manageable, this means that certain routines, such as producing the above scatter plot, can be quite slow.

When high resolution is not needed, we may elect to sample the data to reduce the resulting size. This can be done with the `sample_x3p` function. The function takes a parameter `m` to indicate the sampling factor to use. For example, a value of `m = 4` will sample every 4th height value from the 3D scan, as illustrated in Figure 3.6.

    sample_logo <- sample_x3p(logo, m = 4)

    sample_logo_df <- x3p_to_df(sample_logo)

    ggplot(data = sample_logo_df, aes(x = x, y = y, color = value)) + geom_point() +
        scale_color_gradient(low = "white", high = "black") + theme_bw()

Figure 3.6: A sampled scan of an x3p file extracted using the sample_x3p function.

You can see the clarity of the resulting plot has noticeably declined, but the overall structure has been maintained. Depending on the application, this could be a solution for making a slow analytical process a bit faster.

### 3.3.2 `bulletxtrctr`

As mentioned, we will use the `bulletxtrctr` package to process 3D surface scans of bullets. This package depends on `x3ptools` for reading and writing x3p files but otherwise focuses on statistical routines for matching bullets. The package is not yet available on CRAN, but can be installed from GitHub:

    devtools::install_github("heike/bulletxtrctr")

To demonstrate the functionality of `bulletxtrctr`, we use data from the NBTRD at NIST. We download the surface scan for a bullet from the Hamby Study (Hamby, Brundage, and Thorpe 2009), using the `read_bullet` function, transform the measurements from meters to microns (`x3p_m_to_mum`), and rotate the images so that the long axis is the horizontal. Note that the object `hamby252demo` is a list object exported from `bulletxtrctr` that contains URLs to the NIST NBTRD.

    library(randomForest)
    library(bulletxtrctr)

    # note: length(hamby252demo[[1]]) is 6
    br1_b1 <- read_bullet(urllist = hamby252demo[[1]]) %>% # x3p_m_to_mum: converts from meters to microns
    mutate(x3p = x3p %>% purrr::map(.f = x3p_m_to_mum)) %>% # rotate_x3p(angle = -90: change orientation by 90 degrees clockwise
    # y_flip_x3p: flip image to conform to new ISO norm (see ??y_flip_x3p)
    mutate(x3p = x3p %>% purrr::map(.f = function(x) x %>% rotate_x3p(angle = -90) %>%
        y_flip_x3p()))

When working with lots of bullet data, it’s important to stay organized when naming objects in your R session. The name of the object we just created is `br1_b1`. This indicates that we are looking at the first bullet (`b1`) that was fired from Barrel 1 (`br1`). A bullet is composed of a certain number of land engraved areas (LEAs), and each LEA is a separate file with a separate URL. So, the object `br1_b1` contains `nrow(br1_b1)` (6) observations, one for each land engraved area, which compose the whole bullet scan. The rifling of the barrel induces these land engraved areas, which are a series of alternating raised and recessed portions on the fired bullet. In addition, manufacturing defects engrave striation marks on the bullet as it travels through the gun barrel when fired (AFTE Criteria for Identification Committee 1992b).

Let’s take a quick look at what we see one the first bullet land (Figure 3.7).

    image_x3p(br1_b1$x3p[[1]])

Figure 3.7: Land 1 of Bullet 1 from Barrel 1 of the Hamby Study (Set 44). Source: NRBTD. (Rendered image has been down-sampled to speed up page load.)

Immediately, we can clearly see the vertical striation marks. To better visualize these marks, we can extract a cross-section from the bullet and plot it in two dimensions. To accomplish this, `bulletxtrctr` provides us with a function `x3p_crosscut_optimize` to choose the ideal location at which to do so.

    cc_b11 <- x3p_crosscut_optimize(br1_b1$x3p[[1]])
    cc_b11

    ## [1] 100

This value provides us with the location (in microns) of a horizontal line that the algorithm determines to be a good place to extract a cross-section. The two primary criteria for determining this are:

1. The location should be close to the base of the bullet (\(y = 0\)) because the striation marks are most pronounced there.
2. Cross-sections taken near this location should be similar to this cross-section (stability).

The `x3p_crosscut_optimize` function looks for the first cross-section meeting this criteria, searching upwards from the base of the bullet land. With this value, we can extract and plot the cross-section, shown in Figure 3.8.

    ccdata_b11 <- x3p_crosscut(br1_b1$x3p[[1]], y = cc_b11)

    ggplot(data = ccdata_b11, aes(x = x, y = value)) + geom_line() + theme_bw()

Figure 3.8: Cross-section of the bullet land at the ideal cross-section location.

Most of the scans exhibit the pattern that we see here, where there are “wedges” on the left and right side. The wedge area is called the shoulder, and it is the area separating the land engraved area (the curved region in the middle) from the groove (the area not scanned because it doesn’t exhibit striations). In other words, to better hone in on the striation marks along the land, we should subset this region to include only the middle curved land engraved area portion. Fortunately, `bulletxtrctr` provides us with functionality to automatically do that. First, we use the `cc_locate_grooves` function to detect the location of the grooves. This returns a list object, with one element being the two locations along the axis, and the lother element being the plot, given in Figure 3.9.

    grooves_b11 <- cc_locate_grooves(ccdata_b11, return_plot = TRUE, method = "middle")
    grooves_b11$plot

Figure 3.9: Location of the grooves in our bullet scan, as detected by the `get_grooves` function.

With the grooves detected, we can now smooth out the surface using locally estimated scatter plot smoothing (LOESS) (Cleveland 1979). Once we do so, we obtain what we call a bullet signature, Figure 3.10, representing the clearest picture yet of the striation marks along the surface of the land.

    b111_processed <- cc_get_signature(ccdata = ccdata_b11, grooves = grooves_b11,
        span1 = 0.75, span2 = 0.03) %>% filter(!is.na(sig), !is.na(raw_sig))

    ggplot(data = b111_processed, aes(x = x, y = sig)) + geom_line() + theme_bw()

Figure 3.10: LOESS-smoothed version of our bullet profile, called the bullet signature.

The land signature is the element of analysis for feature extraction out of the `bulletxtrctr` package. With multiple bullet signatures, matches can quickly and easily be made using the `sig_align` function, in conjunction with the `extract_feature` family of functions, which we will discuss later on in the chapter.

## 3.4 Drawing Conclusions

We have seen the process of extracting the signature of a bullet and plotting it using R. But recall that the application of these procedures demands an answer to the question of whether this bullet was fired from the same gun barrel as another bullet. The question becomes, does this bullet signature “match” the signature of another bullet with high probability?

This answer could be derived quite seamlessly in an ideal world given a reference database of all bullets in existence that have been fired from all gun barrels. With this database, we would compute the signatures for all of them and we could then make probabilistic judgments based on the similarities of signatures fired from the same barrel versus those from different barrels. Without this database, the best we can do is to begin a large data collection process resulting in a reference database, such as the approach in the NIST NBTRD. To come to a conclusion about the source of two fired bullets, we need to quantify the similarity of two land signatures that were part of bullets fired from the same barrel. This will be the focus of the Case Study section.

One other approach to drawing conclusions is to use the generated signatures as a supplement to the manual examination by trained forensic examiners. This semi-automated procedure maintains the valuable expertise of the examiner and provides a scientific backing to some of the conclusions made. In the cases where conclusions may differ, this can lead to either refinement of the examination procedure, or refinement of the automated algorithms described.

## 3.5 Case Study

We will now walk through the process of performing a bullet match. Much of the code for this section has been adapted from the excellent `bulletxtrctr` README. We take two bullets with 6 lands each for comparison. Thus, there are 36 land-to-land comparisons to be made, of which 6 are known matches, and 30 are known non-matches. We begin by reading the bullets:

    # bullet 1
    urllist1 <- c("https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/cd204983-465b-4ec3-9da8-cba515a779ff",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/0e72228c-5e39-4a42-8c4e-3da41a11f32c",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/b9d6e187-2de7-44e8-9b88-c83c29a8129d",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/fda92f6a-71ba-4735-ade0-02942d14d1e9",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/8fa798b4-c5bb-40e2-acf4-d9296865e8d4",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/81e817e5-15d8-409f-b5bd-d67c525941fe")
    # bullet 2
    urllist2 <- c("https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/288341e0-0fdf-4b0c-bd26-b31ac8c43f72",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/c97ada55-3a35-44fd-adf3-ac27dd202522",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/8a1805d9-9d01-4427-8873-aef4a0bd323a",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/a116e448-18e1-4500-859c-38a5f5cc38fd",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/0b7182d3-1275-456e-a9b4-ae378105e4af",
        "https://tsapps.nist.gov/NRBTD/Studies/BulletMeasurement/DownloadMeasurement/86934fcd-7317-4c74-86ae-f167dbc2f434")

    b1 <- read_bullet(urllist = urllist1)
    b2 <- read_bullet(urllist = urllist2)

For ease of analysis, we bind the bullets in a single data frame, and identify them using numeric values inside the data frame. We also indicate the six different lands.

    b1$bullet <- 1
    b2$bullet <- 2
    b1$land <- 1:6
    b2$land <- 1:6
    bullets <- rbind(b1, b2)

As before, we want to rotate the bullets such that the long axis is along the horizontal, as the functions within `bulletxtrctr` assume this format.

    bullets <- bullets %>% mutate(x3p = x3p %>% purrr::map(.f = x3p_m_to_mum)) %>%
        mutate(x3p = x3p %>% purrr::map(.f = function(x) x %>% rotate_x3p(angle = -90) %>%
            y_flip_x3p()))

We extract the ideal cross-sections from all 12 bullet lands, which are shown in Figure 3.11. In each land, we see the standard curved pattern, with well defined and a pronounced shoulders indicating the cutoff location for extracting the land.

    bullets <- bullets %>% mutate(crosscut = x3p %>% purrr::map_dbl(.f = x3p_crosscut_optimize))

    bullets <- bullets %>% mutate(ccdata = purrr::map2(.x = x3p, .y = crosscut,
        .f = x3p_crosscut))

    crosscuts <- bullets %>% tidyr::unnest(ccdata)

    ggplot(data = crosscuts, aes(x = x, y = value)) + geom_line() + facet_grid(bullet ~
        land, labeller = "label_both") + theme_bw() + theme(axis.text.x = element_text(angle = 30,
        hjust = 1, vjust = 1, size = rel(0.9)))

Figure 3.11: Ideal cross-sections for all 12 bullet lands.

Next, with each of these profiles, we need to detect grooves to extract the bullet signature between them. In Figure 3.12, we can see that the groove locations of the 12 bullet lands appear to be detected well, such that the middle portion between the two vertical blue lines represents a good sample of the land-engraved area.

    bullets <- bullets %>% mutate(grooves = ccdata %>% purrr::map(.f = cc_locate_grooves,
        method = "middle", adjust = 30, return_plot = TRUE))

    do.call(gridExtra::grid.arrange, lapply(bullets$grooves, `[[`, 2))

Figure 3.12: Groove locations of each of the 12 bullet lands.

With the groove locations detected, we proceed as before by using LOESS to smooth out the curvature of the surface and focus on the striation marks. Figure 3.13 shows us the raw signatures of the 12 lands. The striation marks are much more visible now.

    bullets <- bullets %>% mutate(sigs = purrr::map2(.x = ccdata, .y = grooves,
        .f = function(x, y) {
            cc_get_signature(ccdata = x, grooves = y, span1 = 0.75, span2 = 0.03)
        }))

    signatures <- bullets %>% select(source, sigs) %>% tidyr::unnest()
    bullet_info <- bullets %>% select(source, bullet, land)

    signatures %>% filter(!is.na(sig), !is.na(raw_sig)) %>% left_join(bullet_info,
        by = "source") %>% ggplot(aes(x = x)) + geom_line(aes(y = raw_sig), colour = "grey70") +
        geom_line(aes(y = sig), colour = "grey30") + facet_grid(bullet ~ land, labeller = "label_both") +
        ylab("value") + ylim(c(-5, 5)) + theme_bw()

Figure 3.13: Signatures for the 12 bullet lands. Light gray lines show the raw data, while the dark gray lines are the smoothed signatures.

Because we are working with 12 signatures, our goal will be to align all pairwise comparisons (36 comparisons total) between the six lands in each bullet. Figure 3.14 shows the alignment of Bullet 2 Land 3 with Bullet 1 Land 2, two of the known matches. Immediately it is clear that the pattern of the signatures appears very similar between the two lands.

    bullets$bulletland <- paste0(bullets$bullet, "-", bullets$land)
    lands <- unique(bullets$bulletland)
    comparisons <- data.frame(expand.grid(land1 = lands, land2 = lands), stringsAsFactors = FALSE)

    comparisons <- comparisons %>% mutate(aligned = purrr::map2(.x = land1, .y = land2,
        .f = function(xx, yy) {
            land1 <- bullets$sigs[bullets$bulletland == xx][[1]]
            land2 <- bullets$sigs[bullets$bulletland == yy][[1]]
            land1$bullet <- "first-land"
            land2$bullet <- "second-land"

            sig_align(land1$sig, land2$sig)
        }))

    subset(comparisons, land1 == "2-3" & land2 == "1-2")$aligned[[1]]$lands %>%
        mutate(`b2-l3` = sig1, `b1-l2` = sig2) %>% select(-sig1, -sig2) %>% tidyr::gather(sigs,
        value, `b2-l3`, `b1-l2`) %>% ggplot(aes(x = x, y = value, colour = sigs)) +
        geom_line() + theme_bw() + scale_color_brewer(palette = "Dark2")

Figure 3.14: Alignment of two bullet lands (2-3 & 1-2)

Though the visual evidence is strong, we want to quantify the similarity. To do this, we’re going to use a number of functions which extract features from the aligned signatures of the bullets. We’ll extract the cross-correlation (`extract_feature_ccf`), the matching striation count (`bulletxtrctr:::extract_helper_feature_n_striae`), the non-matching striation count, and many more (`extract_feature_*`).

    comparisons <- comparisons %>% mutate(ccf0 = aligned %>% purrr::map_dbl(.f = function(x) extract_feature_ccf(x$lands)),
        lag0 = aligned %>% purrr::map_dbl(.f = function(x) extract_feature_lag(x$lands)),
        D0 = aligned %>% purrr::map_dbl(.f = function(x) extract_feature_D(x$lands)),
        length0 = aligned %>% purrr::map_dbl(.f = function(x) extract_feature_length(x$lands)),
        overlap0 = aligned %>% purrr::map_dbl(.f = function(x) extract_feature_overlap(x$lands)),
        striae = aligned %>% purrr::map(.f = sig_cms_max, span = 75), cms_per_mm = purrr::map2(striae,
            aligned, .f = function(s, a) {
                extract_feature_cms_per_mm(s$lines, a$lands, resolution = 1.5625)
            }), matches0 = striae %>% purrr::map_dbl(.f = function(s) {
            bulletxtrctr:::extract_helper_feature_n_striae(s$lines, type = "peak",
                match = TRUE)
        }), mismatches0 = striae %>% purrr::map_dbl(.f = function(s) {
            bulletxtrctr:::extract_helper_feature_n_striae(s$lines, type = "peak",
                match = FALSE)
        }), bulletA = gsub("([1-2])-([1-6])", "\\1", land1), bulletB = gsub("([1-2])-([1-6])",
            "\\1", land2), landA = gsub("([1-2])-([1-6])", "\\2", land1), landB = gsub("([1-2])-([1-6])",
            "\\2", land2))

We are now ready to begin matching the bullets. We’ll start by looking at Figure 3.15, which aligns the two bullets by bullet land and colors each of the cells (comparisons) by the cross-correlation function (CCF) value. Encouragingly, we see a diagonal pattern in the matrix, which is to be expected given the assumption that the bullet scans were collected by rotating the bullet and are stored in rotational order. Note that we are also comparing each land to itself (top left and bottom right) in two of the four panels, which as expected exhibit the highest CCF for matches.

    comparisons <- comparisons %>% mutate(features = purrr::map2(.x = aligned, .y = striae,
        .f = extract_features_all, resolution = 1.5625), legacy_features = purrr::map(striae,
        extract_features_all_legacy, resolution = 1.5625)) %>% tidyr::unnest(legacy_features)

    comparisons %>% ggplot(aes(x = landA, y = landB, fill = ccf)) + geom_tile() +
        scale_fill_gradient2(low = "grey80", high = "darkorange", midpoint = 0.5) +
        facet_grid(bulletB ~ bulletA, labeller = "label_both") + xlab("Land A") +
        ylab("Land B") + theme(aspect.ratio = 1)

Figure 3.15: Land-to-Land Comparison of the two bullets colored by the CCF.

We can improve upon these results by using a trained random forest, `bulletxtrctr::rtrees`, which was introduced in Hare, Hofmann, and Carriquiry (2017) in order to assess the probability of a match between bullet lands. Figure 3.16 displays the random forest score, or match probability, of each of the land-to-land comparisons. The results are stronger than using only the CCF in this case.

    comparisons$rfscore <- predict(bulletxtrctr::rtrees, newdata = comparisons,
        type = "prob")[, 2]

    comparisons %>% ggplot(aes(x = landA, y = landB, fill = rfscore)) + geom_tile() +
        scale_fill_gradient2(low = "grey80", high = "darkorange", midpoint = 0.5) +
        facet_grid(bulletB ~ bulletA, labeller = "label_both") + xlab("Land A") +
        ylab("Land B") + theme(aspect.ratio = 1)

Figure 3.16: Random forest matching probabilities of all land-to-land comparisons.

Finally, we can visualize the accuracy of our comparisons by highlighting the cells in which they were in fact matches (same-source). Figure 3.17 shows this, indicating that for the comparison between the two bullets, a couple of the lands didn’t exhibit a high match probability. With that said, given that the other four lands exhibited a strong probability, this is high evidence these bullets were in fact fired from the same barrel. Methods for bullet-to-bullet matching using the random forest results of land-to-land comparisons are still in development at CSAFE. Currently, sequence average matching (SAM) from Sensofar Metrology (2016) is used in similar problems to compare the CCF values in sequence (by rotation of the bullet), and methods in development have been using SAM as a baseline.

Figure 3.17: All Land-to-Land Comparisons of the bullets, highlighting same-source lands.

### References

Biasotti, Alfred A. 1959. “A Statistical Study of the Individual Characteristics of Fired Bullets.” Journal of Forensic Sciences 4 (1): 34–50.

National Research Council. 2009a. Strengthening Forensic Science in the United States: A Path Forward. Washington, DC: The National Academies Press. https://doi.org/10.17226/12589.

OpenFMC. 2014. X3pr: Read/Write Functionality for X3p Surface Metrology Format. https://www.openfmc.org/.

Hofmann, Heike, Susan VanderPlas, Ganesh Krishnan, and Eric Hare. 2018. X3ptools: Tools for Working with 3D Surface Measurements. https://CRAN.R-project.org/package=x3ptools.

Hofmann, Heike, Susan Vanderplas, and Ganesh Krishnan. 2018. Bulletxtrctr: Automatic Matching of Bullet Striae. https://heike.github.io/bulletxtrctr/.

Hare, Eric, Heike Hofmann, and Alicia Carriquiry. 2017. “Automatic Matching of Bullet Land Impressions.” The Annals of Applied Statistics 11 (4): 2332–56.

Hamby, James E., David J. Brundage, and James W. Thorpe. 2009. “The Identification of Bullets Fired from 10 Consecutively Rifled 9mm Ruger Pistol Barrels: A Research Project Involving 507 Participants from 20 Countries.” AFTE Journal 41 (2): 99–110.

AFTE Criteria for Identification Committee. 1992b. “Theory of Identification, Range Striae Comparison Reports and Modified Glossary Definitions – Afte Criteria for Identification Committee Report.” AFTE Journal 24 (2): 336–40.

Cleveland, William S. 1979. “Robust Locally Weighted Regression and Smoothing Scatterplots.” Journal of the American Statistical Association 74 (368): 829–36. http://www.jstor.org/stable/2286407.

Sensofar Metrology. 2016. Https://Www.sensofar.com/Wp-Content/Uploads/2016/06/Appnote-Sensomatch-Bullet-Comparison.pdf.