From 23a04e40f0459e5f7787e748f8d41340f2dfa125 Mon Sep 17 00:00:00 2001 From: Heike Hofmann Date: Tue, 21 May 2024 13:29:29 -0500 Subject: [PATCH] source code --- README.Rmd | 24 +- README.md | 20 +- app/.Rapp.history | 2 + app/Dump/appold.R | 894 ++++++++++++++++++++++++++ app/Dump/bull/PGPD Barrel 9-1 Ld1.x3p | Bin 0 -> 2991563 bytes app/Dump/dump.R | 72 +++ app/Dump/dump2.R | 46 ++ app/Dump/installation.txt | 18 + app/css/styles.css | 203 ++++++ app/images/BulletAnalyzr-Design-2.png | Bin 0 -> 5059 bytes app/images/BulletAnalzr-Mark-2.png | Bin 0 -> 18716 bytes app/server.R | 656 +++++++++++++++++++ app/ui.R | 73 +++ app/ui_inner.R | 70 ++ app/www/csafe_tools_blue.png | Bin 0 -> 124468 bytes app/www/csafe_tools_blue_h.png | Bin 0 -> 98867 bytes app/www/favicon.png | Bin 0 -> 18907 bytes 17 files changed, 2070 insertions(+), 8 deletions(-) create mode 100644 app/.Rapp.history create mode 100755 app/Dump/appold.R create mode 100644 app/Dump/bull/PGPD Barrel 9-1 Ld1.x3p create mode 100644 app/Dump/dump.R create mode 100644 app/Dump/dump2.R create mode 100644 app/Dump/installation.txt create mode 100755 app/css/styles.css create mode 100644 app/images/BulletAnalyzr-Design-2.png create mode 100644 app/images/BulletAnalzr-Mark-2.png create mode 100644 app/server.R create mode 100644 app/ui.R create mode 100644 app/ui_inner.R create mode 100755 app/www/csafe_tools_blue.png create mode 100644 app/www/csafe_tools_blue_h.png create mode 100644 app/www/favicon.png diff --git a/README.Rmd b/README.Rmd index 2c3f0b7..e1b63c4 100644 --- a/README.Rmd +++ b/README.Rmd @@ -29,7 +29,23 @@ are publicly available 3d scans of the Hamby-Brundage bullet set #44 provided by Download: - [Known Bullets 1 and 2 from Barrel 1](examples/Hamby-44/barrel 1.zip) - - Questioned bullets: Bullet [E](examples/Hamby-44/Questioned/Bullet E.zip), [F](examples/Hamby-44/Questioned/Bullet F.zip), - [G](examples/Hamby-44/Questioned/Bullet G.zip), - - + - Questioned bullets: [E](examples/Hamby-44/Questioned//Bullet E.zip), + [F](examples/Hamby-44/Questioned//Bullet F.zip), +[G](examples/Hamby-44/Questioned//Bullet G.zip), +[H](examples/Hamby-44/Questioned//Bullet H.zip), +[I](examples/Hamby-44/Questioned//Bullet I.zip), +[J](examples/Hamby-44/Questioned//Bullet J.zip), +[K](examples/Hamby-44/Questioned//Bullet K.zip), +[L](examples/Hamby-44/Questioned//Bullet L.zip), +[O](examples/Hamby-44/Questioned//Bullet O.zip), +[P](examples/Hamby-44/Questioned//Bullet P.zip), +[S](examples/Hamby-44/Questioned//Bullet S.zip), +[T](examples/Hamby-44/Questioned//Bullet T.zip), +[U](examples/Hamby-44/Questioned//Bullet U.zip), +[X](examples/Hamby-44/Questioned//Bullet X.zip), +[Y](examples/Hamby-44/Questioned//Bullet Y.zip) + + +## Start the Analyzr + +Click on the link to start the app: [BulletAnalyzr](https://labs.omnianalytics.org/bullet-analyzer/) \ No newline at end of file diff --git a/README.md b/README.md index ec9052c..5ee90a8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,19 @@ Download: - [Known Bullets 1 and 2 from Barrel 1](examples/Hamby-44/barrel%201.zip) -- Questioned bullets: Bullet - [E](examples/Hamby-44/Questioned/Bullet%20E.zip), - [F](examples/Hamby-44/Questioned/Bullet%20F.zip), - [G](examples/Hamby-44/Questioned/Bullet%20G.zip), +- Questioned bullets: [E](examples/Hamby-44/Questioned//Bullet%20E.zip), + [F](examples/Hamby-44/Questioned//Bullet%20F.zip), + [G](examples/Hamby-44/Questioned//Bullet%20G.zip), + [H](examples/Hamby-44/Questioned//Bullet%20H.zip), + [I](examples/Hamby-44/Questioned//Bullet%20I.zip), + [J](examples/Hamby-44/Questioned//Bullet%20J.zip), + [K](examples/Hamby-44/Questioned//Bullet%20K.zip), + [L](examples/Hamby-44/Questioned//Bullet%20L.zip), + [O](examples/Hamby-44/Questioned//Bullet%20O.zip), + [P](examples/Hamby-44/Questioned//Bullet%20P.zip), + [S](examples/Hamby-44/Questioned//Bullet%20S.zip), + [T](examples/Hamby-44/Questioned//Bullet%20T.zip), + [U](examples/Hamby-44/Questioned//Bullet%20U.zip), + [X](examples/Hamby-44/Questioned//Bullet%20X.zip), + [Y](examples/Hamby-44/Questioned//Bullet%20Y.zip) + diff --git a/app/.Rapp.history b/app/.Rapp.history new file mode 100644 index 0000000..b39707e --- /dev/null +++ b/app/.Rapp.history @@ -0,0 +1,2 @@ +setwd("~/Desktop/csafe/bullet/bulletanalyzer") +shiny::runApp() diff --git a/app/Dump/appold.R b/app/Dump/appold.R new file mode 100755 index 0000000..729513a --- /dev/null +++ b/app/Dump/appold.R @@ -0,0 +1,894 @@ +## Load Libraries +library(shiny) +library(shinythemes) +library(shinyjs) +library(shinyBS) +library(shinycssloaders) +library(plotly) +library(dplyr) +library(tidyr) +library(bulletr) +library(ggplot2) +library(gridExtra) +library(randomForest) + +options(shiny.maxRequestSize = 30*1024^2) + +## Data Resources +bullet_choices <- file.path("data", "bullets", dir("data/bullets")) +names(bullet_choices) <- basename(bullet_choices) +addResourcePath("images", "images") + +## UI +ui <- fluidPage(title = "BulletAnalyzr", + useShinyjs(), + + tags$head( + tags$link( + href = "https://fonts.googleapis.com/css?family=Montserrat:400,500,700,900|Ubuntu:400,500,700", + rel = "stylesheet", + type = "text/css" + ), + tags$link(rel = "shortcut icon", href = "favicon.png", type = "image/png"), + tags$link(rel = "icon", href = "favicon.png", type = "image/png") + ), + includeCSS("css/styles.css"), + + # Use tags$script to include custom JavaScript + tags$head( + tags$script( + HTML(" + $(document).on('click', '#printBtn', function() { + window.print(); + }); + ") + ) + ), + + tags$div(id="app-container", + fluidRow( + column(width = 4,tags$a(target = "_blank", href="https://forensicstats.org", tags$img(src = "images/BulletAnalzr-Mark-2.png", width="500px"))), + column(width = 4,br()), + column(width = 4,tags$a(target = "_blank", href="https://forensicstats.org", tags$img(src = "images/BulletAnalyzr-Design-2.png", width="500px")),align="right"), + ), + tags$div(id="main-content", + navbarPage(title = NULL,#div(div(id = "img-id",img(src = "csafe_tools_blue.png")),NULL), + tabPanel("Home", + sidebarLayout( + tags$div(id="my-sidebar", sidebarPanel( + # tags$h1(class = "responsive-text", "BULLETANALYZER"), + # br(), + # tags$head(tags$style("#info{font-size: 18px;}")), + + hidden(checkboxInput("stage0", "Stage 0")), + hidden(checkboxInput("stage1", "Stage 1")), + hidden(checkboxInput("stage2", "Stage 2")), + hidden(checkboxInput("stage3", "Stage 3")), + hidden(checkboxInput("stage4", "Stage 4")), + hidden(checkboxInput("stage5", "Stage 5")), + hidden(checkboxInput("stage6", "Stage 6")), + + hidden(checkboxInput("stage00", "Stage 0")), + hidden(checkboxInput("stage11", "Stage 1")), + hidden(checkboxInput("stage22", "Stage 2")), + hidden(checkboxInput("stage33", "Stage 3")), + hidden(checkboxInput("stage44", "Stage 4")), + hidden(checkboxInput("stage55", "Stage 5")), + hidden(checkboxInput("stage66", "Stage 6")), + + shinyjs::hidden( + div(id = "stepstep", + h4("Step-By-Step Procedure"), + helpText("Press the following button to begin the step-by-step version of the algorithm, where each parameter can be tweaked according to your liking."), + actionButton("confirm_stepstep", "Begin Step-By-Step", icon = icon("check")) + ), + + hr() + ), + + div(id = "autonomous", + tags$h1(class = "responsive-text","GET STARTED"), + br(), + helpText("Press the following button to automatically use all the default parameters to get a similarity value for two bullet lands."), + br(), + actionButton("confirm_autonomous", "Begin")#, icon = icon("check")) + ), + + shinyjs::hidden( + div(id = "landselect", + conditionalPanel(condition = "!input.stage0 || input.stage5", + br(), + + h4("Stage 1 Options"), + + selectInput("choose1", "Choose First Land", choices = c("Upload Image", bullet_choices), selected = bullet_choices[5]), + + conditionalPanel(condition = "input.choose1 == 'Upload Image'", + fileInput("file1", "First Bullet Land") + ), + + selectInput("choose2", "Choose Second Land", choices = c("Upload Image", bullet_choices), selected = bullet_choices[7]), + + conditionalPanel(condition = "input.choose2 == 'Upload Image'", + fileInput("file2", "Second Bullet Land") + ), + + actionButton("confirm0", "Generate Report")#, icon = icon("check")), + + # hr() + ) + ) + ), + + conditionalPanel(condition = "input.stage5", hr()), + + conditionalPanel(condition = "input.stage0 && !input.stage1 || input.stage5", + h4("Stage 2 Options"), + + hr(), + + sliderInput("xcoord1", "X Coordinate (First Land)", min = 1, max = 251, value = 136, step = 1), + sliderInput("xcoord2", "X Coordinate (Second Land)", min = 252, max = 502, value = 386, step = 1), + + hr(), + + actionButton("confirm", "Confirm Coordinates", icon = icon("check")), + + # hr(), + + # actionButton("back", "Back to Stage 1", icon = icon("backward")) + ), + + conditionalPanel(condition = "input.stage5", hr()), + + conditionalPanel(condition = "input.stage1 && !input.stage2 || input.stage5", + h4("Stage 3 Options"), + + hr(), + + sliderInput("bounds1", "Coordinate Bounds 1", min = 0, max = 2400, value = c(0, 2400)), + sliderInput("bounds2", "Coordinate Bounds 2", min = 0, max = 2400, value = c(0, 2400)), + + hr(), + + actionButton("confirm2", "Confirm Bounds", icon = icon("check")), + + # hr(), + + # actionButton("back2", "Back to Stage 2", icon = icon("backward")) + ), + + conditionalPanel(condition = "input.stage5", hr()), + + conditionalPanel(condition = "input.stage2 && !input.stage3 || input.stage5", + h4("Stage 4 Options"), + + hr(), + + sliderInput("span", "Loess Span", min = 0.01, max = 0.99, value = 0.03, step = 0.01), + + hr(), + + actionButton("confirm3", "Confirm Span", icon = icon("check")), + + # hr(), + + # actionButton("back3", "Back to Stage 3", icon = icon("backward")) + ), + + conditionalPanel(condition = "input.stage5", hr()), + + conditionalPanel(condition = "input.stage3 && !input.stage4 || input.stage5", + h4("Stage 5 Options"), + + hr(), + + numericInput("alignment", "Alignment", min = -1000, max = 1000, step = 1.5625, value = 0), + + hr(), + + actionButton("confirm4", "Confirm Alignment", icon = icon("check")), + + # hr(), + + # actionButton("back4", "Back to Stage 4", icon = icon("backward")) + ), + + conditionalPanel(condition = "input.stage4", + h4("Stage 6 Options"), + + hr(), + + sliderInput("smoothfactor", "Smoothing Factor", min = 1, max = 100, value = 35, step = 1), + + hr(), + + actionButton("confirm5", "Confirm Smoothing", icon = icon("check")), + + # hr(), + + #actionButton("back5", "Back to Stage 5", icon = icon("backward")) + ), + + conditionalPanel(condition = "input.stage5", + h4("Stage 7 Options"), + + hr(), + + actionButton("confirm6", "Confirm Features", icon = icon("check")), + + # hr(), + + # actionButton("back6", "Back to Stage 6", icon = icon("backward")) + ), + + hidden( + h4("Lighting Options"), + sliderInput("subsample", "Subsample Factor", min = 1, max = 20, value = 2), + sliderInput("ambient_lighting", "Ambient Lighting", min = 0, max = 1, step = 0.1, value = 0.8), + sliderInput("diffuse_lighting", "Diffuse Lighting", min = 0, max = 1, step = 0.1, value = 0.8), + sliderInput("specular_lighting", "Specular Lighting", min = 0, max = 2, step = 0.05, value = 0.05), + sliderInput("roughness_lighting", "Roughness Lighting", min = 0, max = 1, step = 0.1, value = 0.5), + sliderInput("fresnel_lighting", "Fresnel Lighting", min = 0, max = 5, step = 0.1, value = 0.2) + ), + + br(), + conditionalPanel(condition = "input.stage5", + h4("RESULTS EXPORT"), + helpText("Press the following button to export the given results to a PDF file, or print the results."), + actionButton("printBtn", "Print / Save to PDF") + ) + )), + + mainPanel( + conditionalPanel(condition = "input.stage5", + div(class = "center-container", + div(class = "rounded-box", + div(style = "text-align: center; color: #9E1A97; font-size: 32px; font-weight: 700; font-family: 'Montserrat', sans-serif;", "RESULTS:"), + div(style = "text-align: center;", h4(uiOutput("rfpred"))) + ) + ), + + hr(), + + h3("Features"), + hr(), + HTML("Here are the values of the features computed on the aligned bullet signatures."), + + DT::dataTableOutput("features"), + + hr(), + + actionButton("restart", "Restart Algorithm", icon = icon("refresh")), + + hr() + ), + conditionalPanel(condition = "input.stage0 && !input.stage1 || input.stage5", + h3("Stage 2: Finding a Stable Region"), + hr(), + HTML("Below you will find surface topologies of the two bullet lands you have uploaded. You can rotate, pan, zoom, and perform a number of other functions to examine the surfaces.

Our goal is to find a stable region. We want an area of the bullet where there is minimal noise or tank rash, but plenty of pronounced striation markings.

We step through cross-sections of each land at a fixed step size, and uses the CCF (cross-correlation function) to determine stability (a high CCF means that subsequent cross-sections are similar to each other). We begin this procedure near the area where striation markings are typically most pronounced.

We have automatically identified what is believed to be a stable region. You may choose the location to take a cross-section if the algorithm's choice is not satisfactory.") + ), + conditionalPanel(condition = "input.stage1 && !input.stage2 || input.stage5", + h3("Stage 3: Removing Grooves"), + hr(), + HTML("The cross-sections you have taken are shown below. Our next goal will be to remove the grooves, which contain no relevant information for matching, and greatly exceed the size of a typical striation mark. The more accurate we are with groove detection, the less noise we introduce to the remaining steps.

We use a double-pass smoothing method to determine the location of the grooves. We have again attempted to locate the grooves for you, but you may define them yourself. As you adjust the sliders, the plot will automatically update."), + hr(), + + withSpinner(plotOutput("crosssection")) + ), + conditionalPanel(condition = "input.stage2 && !input.stage3 || input.stage5", + h3("Stage 4: Removing Global Structure"), + hr(), + HTML("We have removed the grooves, but the global structure of the cross-section still dominates the overall appearance, making striae more difficult to locate. At this point we are going to fit a loess regression to model this structure. The loess regression includes a span parameter which adjusts the amount of smoothing used. Different values will yield different output. We default to a span of 0.03, but this may be adjusted as desired. "), + hr(), + + withSpinner(plotOutput("loess1")), + withSpinner(plotOutput("loess2")) + ), + conditionalPanel(condition = "input.stage3 && !input.stage4 || input.stage5", + h3("Stage 5: Aligning Signatures"), + hr(), + HTML("The residuals from the loess fit we have extracted in the previous stage are called the bullet signatures. They will form the basis for the rest of the analysis. Since scans are not always taken at a fixed location, and the rotation of the bullet may be slightly less than ideal, the signatures may not automatically `align` in their respective coordinate spaces.

Because the signatures are defined by the residuals, the peaks and valleys visible in this plot represent the striation markings we are looking for. In order to make matching easier, our next step is to align the two signatures.

The alignment parameter defines a horizontal offset that will shift the green graph to the right. The objective is to create perfect alignment between the graphs so that the correlation between the two will be maximized. We suggest an optimal alignment, but it can be adjusted if necessary."), + + plotOutput("alignment") + ), + conditionalPanel(condition = "input.stage4", + h3("Stage 6: Peaks and Valleys"), + hr(), + HTML("With aligned signatures, we now turn our attention to determining what constitutes a peak or a valley. Since there is a lot of noise, this step involves one more smoothing pass. The amount of smoothing reduces the possibility of noisy detections of peaks and valleys in the signature, but too much smoothing can smooth over some real features of the signature.

We can specify a smoothing window, called the smoothing factor, as the number of neighbors to include in the window. For instance, a value of 16 would mean that the nearest 16 points, spanning 16 * 1.5625 = 25 micrometers, would be included."), + + withSpinner(plotOutput("peaks1")), + withSpinner(plotOutput("peaks2")) + ), + #conditionalPanel(condition = "input.stage5", + # h3("Stage 6: Extract Features"), + # hr(), + # HTML("We now have smoothed, aligned bullet signatures with associated peaks and valleys. This gives us a number of features we can extract.

At this point, there is really nothing left to configure about the algorithm. The features extracted are displayed below. The definitions of each can be found in Hare 2016. Press Confirm Features when you are ready to get your predicted probability of a match.")), + # + # dataTableOutput("features") + #), + + + h3("WELCOME TO BULLETANALYZR!"), + p("Our innovation combines 3D imagery and sophisticated algorithms to revolutionize bullet analysis. This prototype demonstrates how our methods can calculate the likelihood of the observed similarity if two bullets originated from the same firearm versus different firearms. It's a work in progress, evolving through feedback from diverse communities."), + br(), + h4("BULLET LAND SURFACES",align="center"), + withSpinner(plotlyOutput("trendPlot", height = "700px")), + hr() + ) + )), + tabPanel( + "About", + h4(HTML("CSAFE Tools is a software suite of state-of-the-art statistical libraries designed to assist practitioners in analyzing forensic data. This work was developed in collaboration with the Center for Statistics and Applications in Forensic Evidence (CSAFE) at Iowa State University and Omni Analytics Group. These procedures are fully open-source and transparent. For more details on the underlying code, please see the GitHub repository for the companion R package.")), + br(), br(), + h4(HTML("This software is an implementation of a front-end to the bulletr package.")), + h4(HTML("This application will walk through the steps used to programmatically determine the probability that two bullets were fired from the same gun. During discharge, as a bullet travels out of the chamber, it is imprinted with a groove signature that is unique to that gun’s barrel. The grooved pattern of a gun’s barrel is so distinct that the striations that are imprinted on a set of fired bullet need only be matched across a small region for there to be statistical confidence of a match; therefore probabilistic comparisons can be made at the bullet land level which represent only one-sixth of a bullet.

+ Hare, E., Hofmann, H., and Carriquiry, A., Algorithmic Approaches to Match Degraded Land Impressions. Law, Probability and Risk, mgx018, https://doi.org/10.1093/lpr/mgx018
+ Hare, E., Hofmann, H., and Carriquiry, A., Automatic Matching of Bullet Land Impressions. Annals of Applied Statistics. doi: 10.1214/17-AOAS1080" + )), + hr() + ), + tabPanel("Instructions",), + tabPanel("Contact",) + ))), + # Footer + tags$div(id="global-footer", + fluidRow( + column(width = 4,tags$img(src="csafe_tools_blue.png", alt="Logo", height = "60px")), + column(width = 4,tags$p("195 Durham Center, 613 Morrill Road, Ames, Iowa, 50011")), + column(width = 4,tags$p("(C) 2023 | All Rights Reserved", class="right-float")) + ) + ) +) + +server <- function(input, output, session) { + # showModal(modalDialog( + # title = "Welcome to BulletAnalyzr! ", + # " Our innovation combines 3D imagery and sophisticated algorithms to revolutionize bullet analysis. This prototype demonstrates how our methods can calculate the likelihood of the observed similarity if two bullets originated from the same firearm versus different firearms. It's a work in progress, evolving through feedback from diverse communities. Click 'Get Started' on the next screen to explore this prototype further.", + # easyClose = TRUE, + # footer = modalButton("Ok") + # )) + + bullet1 <- reactive({ + withProgress(message = "Loading bullet data...", expr = { + if (input$choose1 == "Upload Image") { + if (is.null(input$file1)) return(NULL) + + return(read_x3p(input$file1$datapath)) + } + + return(read_x3p(input$choose1)) + }) + }) + + bullet2 <- reactive({ + withProgress(message = "Loading bullet data...", expr = { + if (input$choose2 == "Upload Image") { + if (is.null(input$file2)) return(NULL) + + return(read_x3p(input$file2$datapath)) + } + + cat(input$file2) + + return(read_x3p(input$choose2)) + }) + }) + + values <- reactiveValues(app_type = "stepstep") + + observeEvent(input$confirm_autonomous, { + values$app_type <- "autonomous" + + shinyjs::hide("stepstep") + shinyjs::hide("confirm_autonomous") + shinyjs::show("landselect") + shinyjs::hide("prelim") + shinyjs::show("land") + }) + + observeEvent(input$confirm_stepstep, { + values$app_type <- "stepstep" + + shinyjs::hide("autonomous") + shinyjs::hide("confirm_stepstep") + shinyjs::show("landselect") + shinyjs::hide("prelim") + shinyjs::show("land") + }) + + observeEvent(input$confirm0, { + shinyjs::hide("autonomous") + shinyjs::hide("stepstep") + + if (!is.null(bullet1()) && !is.null(bullet2())) { + updateCheckboxInput(session, "stage0", value = TRUE) + if (values$app_type == "autonomous") { + updateCheckboxInput(session, "stage00", value = TRUE) + } + } + }) + + observeEvent(input$stage00, { + if (input$confirm0 && input$stage00) { + updateCheckboxInput(session, "stage1", value = TRUE) + updateCheckboxInput(session, "stage11", value = TRUE) + } + }, priority = -1) + + observeEvent(input$stage11, { + if (input$confirm0 && input$stage11 && values$app_type == "autonomous") { + updateCheckboxInput(session, "stage2", value = TRUE) + updateCheckboxInput(session, "stage22", value = TRUE) + } + }, priority = -1) + + observeEvent(input$stage22, { + if (input$confirm0 && input$stage22 && values$app_type == "autonomous") { + updateCheckboxInput(session, "stage3", value = TRUE) + updateCheckboxInput(session, "stage33", value = TRUE) + } + }, priority = -1) + + observeEvent(input$stage33, { + if (input$confirm0 && input$stage33 && values$app_type == "autonomous") { + updateCheckboxInput(session, "stage4", value = TRUE) + updateCheckboxInput(session, "stage44", value = TRUE) + } + }, priority = -1) + + observeEvent(input$stage44, { + if (input$confirm0 && input$stage44 && values$app_type == "autonomous") { + updateCheckboxInput(session, "stage5", value = TRUE) + updateCheckboxInput(session, "stage55", value = TRUE) + } + }, priority = -1) + + observeEvent(input$stage55, { + if (input$confirm0 && input$stage55 && values$app_type == "autonomous") { + updateCheckboxInput(session, "stage6", value = TRUE) + updateCheckboxInput(session, "stage66", value = TRUE) + } + }, priority = -1) + + theSurface <- reactive({ + if (is.null(bullet1()) || is.null(bullet2())) return(NULL) + + b1 <- bullet1() + b2 <- bullet2() + + surf.b1 <- b1[[2]] + surf.b2 <- b2[[2]] + + minrows <- min(nrow(surf.b1), nrow(surf.b2)) + + surf.mat <- cbind(surf.b1[1:minrows,], surf.b2[1:minrows,]) + + x_idx <- seq(1, nrow(surf.mat), by = 2) + y_idx <- seq(1, ncol(surf.mat), by = 2) + + return(surf.mat[x_idx, y_idx]) + }) + + observe({ + updateSliderInput(session, "xcoord1", max = ncol(theSurface()) / 2, value = ncol(theSurface()) / 4) + updateSliderInput(session, "xcoord2", max = ncol(theSurface()), min = 1 + ncol(theSurface()) / 2, value = ncol(theSurface()) * 3 / 4) + }) + + output$trendPlot <- renderPlotly({ + if (is.null(theSurface())) return(NULL) + + p <- plot_ly(z = theSurface(), type = "surface", showscale = FALSE, lighting = list(ambient = input$ambient_lighting, + diffuse = input$diffuse_lighting, + specular = input$specular_lighting, + roughness = input$roughness_lighting, + fresnel = input$fresnel_lighting)) + p + }) + + observeEvent(input$stage0, { + if (!is.null(theSurface()) && input$stage0) { + withProgress(message = "Calculating CCF...", expr = { + crosscut1 <- bulletCheckCrossCut("", + bullet = bullet1(), + xlimits = seq(25, 500, by = 25)) + + crosscut2 <- bulletCheckCrossCut("", + bullet = bullet2(), + xlimits = seq(25, 500, by = 25)) + + updateSliderInput(session, "xcoord1", value = crosscut1) + updateSliderInput(session, "xcoord2", value = crosscut2 + ncol(theSurface()) / 2) + }) + } + }) + + observeEvent(input$confirm, { + updateCheckboxInput(session, "stage1", value = TRUE) + }) + + observeEvent(input$back, { + updateCheckboxInput(session, "stage0", value = FALSE) + }) + + fortified1 <- reactive({ + if (is.null(bullet1()) || !input$stage1) return(NULL) + + bul <- bullet1() + bul[[3]] <- "b1" + names(bul)[3] <- "path" + + return(fortify_x3p(bul)) + }) + + fortified2 <- reactive({ + if (is.null(bullet2()) || !input$stage1) return(NULL) + + bul <- bullet2() + bul[[3]] <- "b2" + names(bul)[3] <- "path" + + return(fortify_x3p(bul)) + }) + + crosscut1 <- reactive({ + if (is.null(bullet1()) || !input$stage1) return(NULL) + + return(get_crosscut(bullet = bullet1(), x = input$xcoord1)) + }) + + crosscut2 <- reactive({ + if (is.null(bullet2()) || !input$stage1) return(NULL) + + return(get_crosscut(bullet = bullet2(), x = input$xcoord2 - ncol(theSurface()) / 2)) + }) + + observe({ + if (!is.null(fortified1()) && !is.null(fortified2())) { + updateSliderInput(session, "bounds1", max = floor(max(fortified1()$y)), value = c(0, floor(max(fortified1()$y)))) + updateSliderInput(session, "bounds2", max = floor(max(fortified2()$y)), value = c(0, floor(max(fortified2()$y)))) + } + }) + + observeEvent(input$stage1, { + if (!is.null(crosscut1()) && !is.null(crosscut2())) { + + withProgress(message = "Locating grooves...", expr = { + groove1 <- get_grooves(crosscut1()) + groove2 <- get_grooves(crosscut2()) + + updateSliderInput(session, "bounds1", value = groove1$groove) + updateSliderInput(session, "bounds2", value = groove2$groove) + }) + } + }) + + output$crosssection <- renderPlot({ + if (is.null(fortified1()) || is.null(fortified2())) return(NULL) + + fortified <- fortified1() + fortified2 <- fortified2() + + myx <- unique(fortified$x) + xval <- myx[which.min(abs(myx - input$xcoord1))] + myx2 <- unique(fortified2$x) + xval2 <- myx2[which.min(abs(myx2 - (input$xcoord2 - ncol(theSurface()) / 2)))] + + plotdat <- fortified %>% + filter(x == xval) %>% + select(-x) %>% + full_join( + fortified2 %>% + filter(x == xval2) %>% + select(-x) + , by = c("y" = "y")) %>% + rename(bullet1 = value.x, bullet2 = value.y) %>% + gather(key = bullet, value = value, bullet1:bullet2) + + plotdat$include <- FALSE + plotdat$include[plotdat$bullet == "bullet1"] <- (plotdat$y[plotdat$bullet == "bullet1"] >= input$bounds1[1] & plotdat$y[plotdat$bullet == "bullet1"] <= input$bounds1[2]) + plotdat$include[plotdat$bullet == "bullet2"] <- (plotdat$y[plotdat$bullet == "bullet2"] >= input$bounds2[1] & plotdat$y[plotdat$bullet == "bullet2"] <= input$bounds2[2]) + + vline.data <- data.frame(zleft = c(input$bounds1[1], input$bounds2[1]), + zright = c(input$bounds1[2], input$bounds2[2]), + bullet = c("bullet1", "bullet2")) + + ggplot(data = plotdat, aes(x = y, y = value, alpha = include)) + + facet_wrap(~bullet, nrow = 2) + + geom_vline(aes(xintercept = zleft), colour = "blue", data = vline.data) + + geom_vline(aes(xintercept = zright), colour = "blue", data = vline.data) + + geom_line(linewidth = 1) + + xlim(c(0, max(plotdat$y))) + + theme_bw() + }) + + observeEvent(input$confirm2, { + updateCheckboxInput(session, "stage2", value = TRUE) + }) + + observeEvent(input$back2, { + updateCheckboxInput(session, "stage1", value = FALSE) + }) + + loess1 <- reactive({ + if (is.null(crosscut1()) || !input$stage2) return(NULL) + + return(fit_loess(bullet = crosscut1(), groove = list(groove = input$bounds1), span = input$span)) + }) + + loess2 <- reactive({ + if (is.null(crosscut2()) || !input$stage2) return(NULL) + + return(fit_loess(bullet = crosscut2(), groove = list(groove = input$bounds2), span = input$span)) + }) + + processed1 <- reactive({ + if (is.null(fortified1()) || !input$stage2) return(NULL) + + myx <- unique(fortified1()$x) + xval <- myx[which.min(abs(myx - input$xcoord1))] + + processBullets(bullet = bullet1(), name = "b1", x = xval, grooves = input$bounds1) + }) + + processed2 <- reactive({ + if (is.null(fortified2()) || !input$stage2) return(NULL) + + myx <- unique(fortified2()$x) + xval <- myx[which.min(abs(myx - (input$xcoord2 - ncol(theSurface()) / 2)))] + + processBullets(bullet = bullet2(), name = "b2", x = xval, grooves = input$bounds2) + }) + + smoothed <- reactive({ + if (is.null(processed1()) || is.null(processed2())) return(NULL) + + bullets_processed <- list(b1 = processed1(), b2 = processed2()) + + result <- bullets_processed %>% bind_rows %>% bulletSmooth(span = input$span) + result$bullet <- c(rep("b1", nrow(processed1())), rep("b2", nrow(processed2()))) + + return(result) + }) + + output$loess1 <- renderPlot({ + if (is.null(loess1()) || is.null(smoothed())) return(NULL) + + withProgress(message = "Loading plots...", { + p1 <- ggplot(data = filter(smoothed(), bullet == "b1"), aes(x = y, y = l30)) + + geom_line() + + theme_bw() + + grid.arrange(loess1()$fitted, p1, ncol = 2) + }) + }) + + output$loess2 <- renderPlot({ + if (is.null(loess2()) || is.null(smoothed())) return(NULL) + + withProgress(message = "Loading plots...", { + p2 <- ggplot(data = filter(smoothed(), bullet == "b2"), aes(x = y, y = l30)) + + geom_line() + + theme_bw() + + grid.arrange(loess2()$fitted, p2, ncol = 2) + }) + }) + + observeEvent(input$confirm3, { + updateCheckboxInput(session, "stage3", value = TRUE) + }) + + observeEvent(input$back3, { + updateCheckboxInput(session, "stage2", value = FALSE) + }) + + myalign <- reactive({ + if (is.null(smoothed())) return(NULL) + + bulletAlign(data = smoothed()) + }) + + observeEvent(input$stage3, { + if (!is.null(myalign())) { + withProgress(message = "Determining alignment...", expr = { + updateSliderInput(session, "alignment", value = myalign()$lag) + }) + } + }) + + chosenalign <- reactive({ + if (is.null(myalign())) return(NULL) + + alignval <- round(input$alignment / 1.5625, digits = 0) * 1.5625 + + chosen <- myalign() + chosen$lag <- alignval + chosen$bullets$y[chosen$bullets$bullet == "b2"] <- chosen$bullets$y[chosen$bullets$bullet == "b2"] - min(chosen$bullets$y[chosen$bullets$bullet == "b2"]) + chosen$lag + + return(chosen) + }) + + output$alignment <- renderPlot({ + if (is.null(chosenalign())) return(NULL) + + mydat <- chosenalign()$bullets + + ggplot(data = mydat, aes(x = y, y = l30, colour = bullet, alpha = I(0.8))) + + geom_line() + + theme(legend.position = "bottom") + + theme_bw() + }) + + observeEvent(input$confirm4, { + updateCheckboxInput(session, "stage4", value = TRUE) + }) + + observeEvent(input$back4, { + updateCheckboxInput(session, "stage3", value = FALSE) + }) + + peaks1 <- reactive({ + if (is.null(chosenalign()) || !input$stage4) return(NULL) + + bAlign <- chosenalign() + lofX <- bAlign$bullet + + return(get_peaks(subset(lofX, bullet == "b1"), smoothfactor = input$smoothfactor)) + }) + + peaks2 <- reactive({ + if (is.null(chosenalign()) || !input$stage4) return(NULL) + + bAlign <- chosenalign() + lofX <- bAlign$bullet + + return(get_peaks(subset(lofX, bullet == "b2"), smoothfactor = input$smoothfactor)) + }) + + output$peaks1 <- renderPlot({ + if (is.null(peaks1())) return(NULL) + + return(peaks1()$plot) + }) + + output$peaks2 <- renderPlot({ + if (is.null(peaks2())) return(NULL) + + return(peaks2()$plot) + }) + + CMS <- reactive({ + if (is.null(peaks1()) || is.null(peaks2())) return(NULL) + + bAlign <- chosenalign() + lofX <- bAlign$bullet + + peaks1 <- peaks1() + peaks2 <- peaks2() + + peaks1$lines$bullet <- "b1" + peaks2$lines$bullet <- "b2" + + lines <- striation_identify(peaks1$lines, peaks2$lines) + maxCMS <- maxCMS(lines$match == TRUE) + list(maxCMS = maxCMS, ccf = bAlign$ccf, lag = bAlign$lag, + lines = lines, bullets = lofX) + }) + + features <- reactive({ + if (is.null(CMS())) return(NULL) + + res <- CMS() + + lofX <- res$bullets + aligned <- chosenalign() + b12 <- unique(lofX$bullet) + + subLOFx1 <- subset(aligned$bullets, bullet==b12[1]) + subLOFx2 <- subset(aligned$bullets, bullet==b12[2]) + + ys <- intersect(subLOFx1$y, subLOFx2$y) + idx1 <- which(subLOFx1$y %in% ys) + idx2 <- which(subLOFx2$y %in% ys) + distr.dist <- mean((subLOFx1$val[idx1] - subLOFx2$val[idx2])^2, na.rm=TRUE) + distr.sd <- sd(subLOFx1$val, na.rm=TRUE) + sd(subLOFx2$val, na.rm=TRUE) + km <- which(res$lines$match) + knm <- which(!res$lines$match) + if (length(km) == 0) km <- c(length(knm)+1,0) + if (length(knm) == 0) knm <- c(length(km)+1,0) + # browser() + # feature extraction + + signature.length <- min(nrow(subLOFx1), nrow(subLOFx2)) + + data.frame(ccf=res$ccf, lag=res$lag, + D=distr.dist, + sd.D = distr.sd, + b1=b12[1], b2=b12[2], x1 = subLOFx1$x[1], x2 = subLOFx2$x[1], + #num.matches = sum(res$lines$match), + signature.length = signature.length, + matches.per.y = sum(res$lines$match) / signature.length, + #num.mismatches = sum(!res$lines$match), + mismatches.per.y = sum(!res$lines$match) / signature.length, + #cms = res$maxCMS, + cms.per.y = res$maxCMS / signature.length, + #cms2 = bulletr::maxCMS(subset(res$lines, type==1 | is.na(type))$match), + cms2.per.y = bulletr::maxCMS(subset(res$lines, type==1 | is.na(type))$match) / signature.length, + #non_cms = bulletr::maxCMS(!res$lines$match), + non_cms.per.y = bulletr::maxCMS(!res$lines$match) / signature.length, + #left_cms = max(knm[1] - km[1], 0), + left_cms.per.y = max(knm[1] - km[1], 0) / signature.length, + #right_cms = max(km[length(km)] - knm[length(knm)],0), + right_cms.per.y = max(km[length(km)] - knm[length(knm)],0) / signature.length, + #left_noncms = max(km[1] - knm[1], 0), + left_noncms.per.y = max(km[1] - knm[1], 0) / signature.length, + #right_noncms = max(knm[length(knm)]-km[length(km)],0), + right_noncms.per.y = max(knm[length(knm)]-km[length(km)],0) / signature.length, + #sumpeaks = sum(abs(res$lines$heights[res$lines$match])), + sumpeaks.per.y = sum(abs(res$lines$heights[res$lines$match])) / signature.length + ) + }) + + output$features <- DT::renderDataTable({ + if (is.null(features())) return(NULL) + + result <- as.data.frame(t(features())) + result <- cbind(Feature = rownames(result), result) + names(result)[2] <- "Value" + + clean_result <- result %>% + filter(Feature %in% c("ccf", "D", "signature.length", "matches.per.y", + "mismatches.per.y", "cms.per.y", "non_cms.per.y", + "sumpeaks.per.y")) %>% + mutate(Feature = c("CCF", "D", "Signature Length in Millimeters", "Matches Per Millimeter", + "Mismatches Per Millimeter", "CMS Per Millimeter", + "Non-CMS Per Millimeter", "Peak Sum Per Millimeter"), + Value = c(as.numeric(as.character(Value[1:2])), as.numeric(as.character(Value[3])) / 1000 * 1.5625, as.numeric(as.character(Value[4:8])) / 1.5625 * 1000)) + + clean_result$Value <- sprintf("%.4f", clean_result$Value) + + return(as_tibble(clean_result)) + }) + + observeEvent(input$confirm5, { + updateCheckboxInput(session, "stage5", value = TRUE) + }) + + observeEvent(input$back5, { + updateCheckboxInput(session, "stage4", value = FALSE) + }) + + observeEvent(input$confirm6, { + updateCheckboxInput(session, "stage6", value = TRUE) + }) + + observeEvent(input$back6, { + updateCheckboxInput(session, "stage5", value = FALSE) + }) + + output$rfpred <- renderText({ + if (is.null(features())) return(NULL) + + features <- features() + features$b1 <- gsub(".x3p", "", basename(as.character(features$b1))) + features$b2 <- gsub(".x3p", "", basename(as.character(features$b2))) + features$span <- span + + includes <- setdiff(names(features), c("b1", "b2", "data", "resID", "id.x", "id.y", "pred", "span", "forest")) + + load("data/rf.RData") + + matchprob <- sprintf("%.4f", predict(rtrees, newdata = features[,includes], type = "prob")[,2]) + if (matchprob == "0.0000") matchprob <- "< .0001" else if (matchprob == "1.0000") matchprob <- "> .9999" + + return(HTML(paste0("The same source similarity value is
", h1(matchprob)))) + }) + + observeEvent(input$restart, { + session$reload() + }) + +} + +shinyApp(ui = ui, server = server) diff --git a/app/Dump/bull/PGPD Barrel 9-1 Ld1.x3p b/app/Dump/bull/PGPD Barrel 9-1 Ld1.x3p new file mode 100644 index 0000000000000000000000000000000000000000..2a90d47a84976eb2b1e7033faa4c8b877af1e279 GIT binary patch literal 2991563 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