You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Shiny仪表盘中计算点击两点间距离并实现像素转厘米

解决Shiny仪表板中线段距离计算(像素转厘米)的方案

核心思路

Shiny环境下locator()无法正常工作,改用plotOutput的click事件捕获点击坐标,通过reactiveVal存储配对的端点,计算像素距离后结合预设比例尺转换为厘米。

完整实现代码

library(shiny)
library(ggplot2)
library(png) # 需先安装:install.packages("png")

# 配置参数:替换为你的图像路径,以及校准后的比例尺
image_path <- "your_image.png"
# 比例尺示例:假设图像中10厘米对应500像素,则scale_cm_per_pixel = 10/500 = 0.02
scale_cm_per_pixel <- 0.02

ui <- fluidPage(
  titlePanel("图像线段距离测量"),
  sidebarLayout(
    sidebarPanel(
      actionButton("reset_btn", "重置所有点"),
      actionButton("calc_btn", "计算线段距离"),
      verbatimTextOutput("distance_result")
    ),
    mainPanel(
      plotOutput("image_plot", click = "plot_click", height = "600px")
    )
  )
)

server <- function(input, output, session) {
  # 存储点击的端点列表,每两个元素为一组线段
  clicked_points <- reactiveVal(list())
  
  # 渲染图像及已选点/线段
  output$image_plot <- renderPlot({
    img <- readPNG(image_path)
    img_dim <- dim(img)
    ggplot() +
      annotation_raster(img, xmin = 0, xmax = img_dim[2], ymin = 0, ymax = img_dim[1]) +
      theme_void() +
      # 绘制红色端点
      geom_point(data = do.call(rbind, clicked_points()), aes(x, y), color = "#ff3333", size = 4) +
      # 绘制蓝色线段
      geom_segment(data = get_segments(), aes(x = x1, y = y1, xend = x2, yend = y2), color = "#3366ff", size = 1.2)
  })
  
  # 处理点击事件,成对存储端点
  observeEvent(input$plot_click, {
    current <- clicked_points()
    new_pt <- data.frame(x = input$plot_click$x, y = input$plot_click$y)
    clicked_points(c(current, list(new_pt)))
  })
  
  # 生成线段数据框,用于绘图
  get_segments <- reactive({
    pts <- clicked_points()
    if (length(pts) < 2) return(data.frame())
    
    # 每两个点组成一条线段
    lapply(seq(1, length(pts), 2), function(i) {
      if (i + 1 <= length(pts)) {
        data.frame(
          x1 = pts[[i]]$x, y1 = pts[[i]]$y,
          x2 = pts[[i+1]]$x, y2 = pts[[i+1]]$y
        )
      }
    }) %>% do.call(rbind, .)
  })
  
  # 计算并输出距离
  observeEvent(input$calc_btn, {
    segments <- get_segments()
    if (nrow(segments) == 0) {
      output$distance_result <- renderPrint(cat("请先点击选择至少一对端点\n"))
      return()
    }
    
    # 计算每条线段的像素距离,转换为厘米
    dist_cm <- apply(segments, 1, function(row) {
      pixel_dist <- sqrt((row["x2"] - row["x1"])^2 + (row["y2"] - row["y1"])^2)
      round(pixel_dist * scale_cm_per_pixel, 2)
    })
    
    output$distance_result <- renderPrint({
      cat("各线段距离(厘米):\n")
      for (i in seq_along(dist_cm)) {
        cat(sprintf("线段 %d: %.2f cm\n", i, dist_cm[i]))
      }
    })
  })
  
  # 重置所有点和结果
  observeEvent(input$reset_btn, {
    clicked_points(list())
    output$distance_result <- renderPrint(cat("已重置所有选择\n"))
  })
}

shinyApp(ui, server)

关键细节说明

  1. 替代locator:通过plotOutput(click = "plot_click")捕获点击坐标,input$plot_click$x和input$plot_click$y对应图像的像素位置(x为列数,y为行数)。
  2. 端点配对:用reactiveVal存储点击的点列表,每两次点击自动组成一条线段的两个端点。
  3. 比例尺校准:scale_cm_per_pixel需要根据实际图像校准——在图像中放入已知长度的参照物(如10cm尺子),测量其像素长度后,计算scale_cm_per_pixel = 实际长度 / 像素长度。
  4. 可视化反馈:实时绘制红色端点和蓝色线段,直观展示已选择的测量线段。

使用步骤

  1. 替换image_path为你的图像路径。
  2. 校准scale_cm_per_pixel参数。
  3. 启动App后,依次点击线段的两个端点,每对点自动组成一条线段。
  4. 点击「计算线段距离」按钮查看结果,点击「重置所有点」清除选择。

内容的提问来源于stack exchange,提问作者Felipe Kowalski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 20:50:28