将来自耦合显微镜/光谱学的图像和数据拼接到Photoshop或R中的全景图像中

我有一组图像和X射线数据,这些图像和X射线数据是由耦合扫描电子显微镜和能量色散光谱学产生的。这是我的问题:

我像这样对岩石表面的断面成像(紫色框勾勒出断面区域):

将来自耦合显微镜/光谱学的图像和数据拼接到Photoshop或R中的全景图像中

我想要非常高分辨率,所以我使用3000倍放大率的7张图像进行了此处理,并将它们与Photoshop中的photomerge脚本拼接在一起。这是单个图像的示例:

将来自耦合显微镜/光谱学的图像和数据拼接到Photoshop或R中的全景图像中

及其在光合并图像样线中的位置:

将来自耦合显微镜/光谱学的图像和数据拼接到Photoshop或R中的全景图像中

在这7个位置的每个位置,我还收集了X射线数据,这些数据会为每个检测到的元素生成一个元素图,并将其写入TIFF。我还希望将每个元素贴图TIFF缝合在一起,以便可以将其叠加在岩石的合并断面图像上。这是我想要的结果:

将来自耦合显微镜/光谱学的图像和数据拼接到Photoshop或R中的全景图像中

问题在于元素映射中没有足够的功能,无法通过光合并将它们缝合在一起。它基本上是二进制的-如果检测到元素,则像素为某种颜色(在我的示例图像中,像素为红色(对于铁来说是红色,对于硫磺来说是黄色)),或者如果没有检测到元素,则为黑色。您可以看到元素映射的大部分都是黑色的。

我现在有大约20个样面x 7个图像,每个x 10个元素,这导致需要将大约1400个图像放在一起,因此需要自动化。

我的想法是用photomerge将岩石图像缝合在一起。 photomerge的输出是一个智能对象,其中每个图像都是一个图层。然后,我将使用脚本来获取光合并图像对象中的7张图像的左上角坐标,宽度和高度。然后,我将这些属性放置并分配给7个图像的每个对应元素图,以生成“合并的”元素图以覆盖在图像上。我本人尝试进行此工作,但我不精通javascript,无法将头放在Photoshop API上。

我在Github here上上传了一个示例数据集。 7个样点位置从左到右分别为:-2,-1、0、1、2、3、4。其中有岩石和子目录的图像,每个位置都有元素数据。

ngd267cui 回答:将来自耦合显微镜/光谱学的图像和数据拼接到Photoshop或R中的全景图像中

我不知道Photoshop或R,JavaScript也是如此:

const names = { // map from directory names to patterns (where "#" stands for position index) of names of images therein
 "SEM_images" : "pos# image.tif","Al" : "Al Kα1 pos# map data.tif","Ba" : "Ba Lα1 pos# map data.tif","C"  : "C Kα1_2 pos# map data.tif","Ca" : "Ca Kα1 pos# map data.tif","Fe" : "Fe Kα1 pos# map data.tif","Hg" : "Hg Lα1 pos# map data.tif","Ir" : "Ir Lα1 pos# map data.tif","K"  : "K Kα1 pos# map data.tif","Mg" : "Mg Kα1_2 pos# map data.tif","Mn" : "Mn Kα1 pos# map data.tif","Na" : "Na Kα1_2 pos# map data.tif","O"  : "O Kα1 pos# map data.tif","Os" : "Os Lα1 pos# map data.tif","P"  : "P Kα1 pos# map data.tif","S"  : "S Kα1 pos# map data.tif","Si" : "Si Kα1 pos# map data.tif","Ti" : "Ti Kα1 pos# map data.tif"
}

const SCALE = 1/10 // scale of output images

const OVERLAP = 1.0 // minimum *tested* (horizontal) overlap of images relative to their width
const H_BOX = 0.1 // height of comparison box relative to height of images
const W_BOX = 0.1 // width  of comparison box relative to width  of images
const ADJUSTMENT = 0 // (vertical) adjustment of comparison box [pixels]

/* Merge images given:
 * dataset - dataset address as String
 * directory - directory name for images as String
 * pattern - pattern (where "#" stands for position index) of names of images in directory
 * pos_min - minimum position index of images as Number
 * pos_max - maximum position index of images as Number
 */
function Merge(dataset,directory,pos_min,pos_max) {
  if (dataset[dataset.length - 1] != "/") dataset += "/"
  const images = []
  for (let pos = pos_min; pos <= pos_max; ++pos) (images[pos - pos_min] = new Image).src = dataset + directory + "/" + names[directory].replace("#",pos)
  merge(images,dataset,pos_max)
}

function Laplacian(imagedata) { // 5-point stencil approximation
  const data = imagedata.data
  const L = data.length/4
  const grayscale = new Float32Array(L)
  for (let i = 0; i < L; ++i) {
    const I = 4*i
    grayscale[i] = (data[I    ] + data[I + 1] + data[I + 2])/3
  }
  const Laplacian = new Float32Array(L)
  //const H = imagedata.height
  const Hm1 = imagedata.height - 1
  const W = imagedata.width
  const Wm1 = W - 1
  for (let r = 1; r < Hm1; ++r) {
    const R = r*W
    for (let c = 1; c < Wm1; ++c) {
      const i = R + c
      Laplacian[i] = grayscale[i - W] + grayscale[i + W] + grayscale[i - 1] + grayscale[i + 1] - 4*grayscale[i]
    }
  }
  for (let c = 1; c < Wm1; ++c) {
    //const i = c
    Laplacian[c] = grayscale[c + W] + grayscale[c - 1] + grayscale[c + 1] - 4*grayscale[c]
  }
  for (let c = 1; c < Wm1; ++c) {
    const i = Hm1*W + c
    Laplacian[i] = grayscale[i - W] + grayscale[i - 1] + grayscale[i + 1] - 4*grayscale[i]
  }
  for (let r = 1; r < Hm1; ++r) {
    const i = r*W
    Laplacian[i] = grayscale[i - W] + grayscale[i + W] + grayscale[i + 1] - 4*grayscale[i]
  }
  for (let r = 1; r < Hm1; ++r) {
    const i = r*W + Wm1
    Laplacian[i] = grayscale[i - W] + grayscale[i + W] + grayscale[i - 1] - 4*grayscale[i]
  }
  {
    const Lm1 = L - 1
    const LmW = L - W
    Laplacian[0  ] = grayscale[W      ] + grayscale[1      ] - 4*grayscale[0  ]
    Laplacian[W  ] = grayscale[2*W    ] + grayscale[Wm1    ] - 4*grayscale[W  ]
    Laplacian[LmW] = grayscale[LmW - W] + grayscale[LmW + 1] - 4*grayscale[LmW]
    Laplacian[Lm1] = grayscale[Lm1 - W] + grayscale[Lm1 - 1] - 4*grayscale[Lm1]
  }
  return Laplacian
}

function merge(images,pos_max) {
  for (const image of images) if (!image.complete) {
    setTimeout(merge,1000,images,pos_max) // wait 1000ms = 1s
    return
  }
  let Row,Col
  const Coords = [[Row = 0,Col = 0]]
  let index = 0
  let image = images[index]
  const H = image.naturalHeight
  const W = image.naturalWidth
  if (W*H == 0) return []
  const canvas = document.createElement("canvas")
  canvas.height = H
  canvas.width  = W
  const context = canvas.getContext('2d')
  context.drawImage(image,0)
  let prev = Laplacian(context.getImageData(0,W,H))
  const length = images.length
  const h = Math.round(H_BOX*H)
  const Hmh = H - h
  const w = Math.round(W_BOX*W)
  const o = Math.max(Math.round((1 - OVERLAP)*W),w)
  const Wmw = W - w
  const row_offset = Math.round(Hmh/2) + ADJUSTMENT
  const offset = row_offset*W
  for (++index; index < length; ++index) {
    image = images[index]
    if (image.naturalHeight != H || image.naturalWidth != W) alert("Dimension mismatch: " + image.src)
    context.drawImage(image,0)
    const curr = Laplacian(context.getImageData(0,H))
    let max = -1
    let row,col
    for (let r = 0; r < Hmh; ++r) {
      const R = r*W
      for (let c = o; c < Wmw; ++c) {
        let m = 0
        for (let i = 0; i < h; ++i) {
          const I = i*W
          const K = R + I + c
          const k = offset + I
          for (let j = 0; j < w; ++j) if (prev[K + j]*curr[k + j] > 0) ++m
        }
        if (m > max) {
          max = m
          row = r
          col = c
        }
      }
    }
    Coords[index] = [(Row += row - row_offset)/H,(Col += col)/W]
    prev = curr
  }
  Stitch(dataset,pos_max,Coords)
}

function Stitch(dataset,Coords) {
  if (dataset[dataset.length - 1] != "/") dataset += "/"
  document.body.appendChild(document.createElement("h1")).innerText = `${dataset} :[${pos_min},${pos_max}] @${JSON.stringify(Coords)}`
  const tasks = []
  for (const directory in names) {
    document.body.appendChild(document.createElement("h2")).innerText = directory
    const images = []
    for (let pos = pos_min; pos <= pos_max; ++pos) (images[pos - pos_min] = new Image).src = dataset + directory + "/" + names[directory].replace("#",pos)
    const target = document.body.appendChild(document.createElement("img"))
    target.height = 0
    target.width  = 0
    tasks.push([images,target])
  }
  process(tasks,Coords)
}

const ROW = 0
const COL = 1
function stitch(images,Coords) {
  let image
  let index
  for (index in images) {
    image = images[index]
    if (image.naturalHeight != 0 && image.naturalWidth != 0) break
  }
  const H = image.naturalHeight
  const W = image.naturalWidth
  const canvas = document.createElement("canvas")
  let r_min = 0
  let r_max = 0
  let c_min = 0
  let c_max = 0
  for (coords of Coords) {
    const r = coords[ROW]
    const c = coords[COL]
    if (r < r_min) r_min = r
    if (r > r_max) r_max = r
    if (c < c_min) c_min = c
    if (c > c_max) c_max = c
  }
  canvas.height = (r_max - r_min + 1)*H
  canvas.width  = (c_max - c_min + 1)*W
  const context = canvas.getContext('2d')
  if (context == null) {
    let list = ""
    for (const image of images) list += "\n- " + image.src
    alert("Too large: stitching area required for:" + list)
    return
  }
  let coords = Coords[index]
  let row = (coords[ROW] - r_min)*H
  let col = (coords[COL] - c_min)*W
  context.drawImage(image,col,row)
  const length = images.length
  for (++index; index < length; ++index) {
    image = images[index]
    if (image.naturalHeight == 0 || image.naturalWidth == 0) continue
    if (image.naturalHeight != H || image.naturalWidth != W) alert("Dimension mismatch: " + image.src)
    coords = Coords[index]
    row = coords[ROW]*H
    col = coords[COL]*W
    context.drawImage(image,row)
  }
  return canvas.toDataURL()
}

function process(tasks,Coords) {
  const task = tasks.shift()
  const images = task[0]
  for (const image of images) if (!image.complete) {
    tasks.push(task)
    setTimeout(process,tasks,Coords) // wait 1000ms = 1s
    return
  }
  const target = task[1]
  target.src = stitch(images,Coords)
  target.onload = function () {
    this.height = SCALE*this.naturalHeight
    this.width  = SCALE*this.naturalWidth
    this.style = "border: solid black 1px"
  }
  if (tasks.length > 0) process(tasks,Coords)
}

要运行,请执行以下操作:

Merge("https://raw.githubusercontent.com/CaitlinCasar/dataStitcher/master/example_dataset/","SEM_images",-2,4)

带有Fe覆盖层的SEM_images示例: SEM_images with Fe overlay

本文链接:https://www.f2er.com/2650301.html

大家都在问