一种制造三维阵列的2D横截面切片 [英] Making 2D cross-sectional slices of a 3D array

查看:124
本文介绍了一种制造三维阵列的2D横截面切片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图让从三维CT数据的实时模拟超声图像。诀窍是,用户控制探头,它定义他们看到飞机的位置。

我所做的,到目前为止是读取所有的DICOM图像的像素数据为像素的单个3D阵列,而现在我需要做的是重新分区的3D阵列在不同的角度。很抱歉,如果下面的描述中脱落有点草率,但是想象一下,一个三维的矩形框(说100像素宽而深[X,Z],500长[Y])和2D看飞机(比如50×50像素)。说(定义为中间点,在飞机的接近边缘原点 - [0,25])查看平面的起始位置与原点[50,250,0](死顶面的中心,俯视) ,面向左到右,穿出矩形直降。因此,该观测平面具有可以改变三个参数 - 的原点的位置,围绕垂直(从原点到在飞机上的相对边缘上的相应点上运行的线)的旋转,和倾斜(围绕它与盒相交)的线的平面的旋转。因此,用户可以改变这些三个参数,并输出由观看平面从接触的象素建立的图像。

此外,我道歉,如果说明是草率的,但我是一个活蹦乱跳的未婚大学生没有强大的数学背景。任何帮助将大大AP preciated。

解决方案
  

我会写二维方程一条线,求出x的每一个值,   而圆产生的变量y到最近的整型Edje09昨天

坚持到2D情况下的时候,你建议的方法有两个主要的问题。

  1. 如果该行是比1,那么一些像素可以被错过梯度更陡。
  2. 四舍五入可以选择上面你想选择一个像素。

<一个href="https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxqa2dhZGdldHN0b3JlfGd4OjZjN2U4YTQ2NWVhZGI5Yjk"相对=nofollow>此PDF格式的显示的问题,并为2D情况,然后可以基于为三维的情况下可能的解决方案。

编辑进一步的思考后,我可能会产生一个<一个href="https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxqa2dhZGdldHN0b3JlfGd4OjVmNWIyNzc0MDZmYjE5OGY"相对=nofollow>写的PDF大纲为3D的情况下该解决方案可以变成一种算法,从而为code。这是据我已经得到了我没有做过检查,并不能机制保障它的正确性,但希望将带您一个阶段进一步。

编辑$ C $下加入 下面的JavaScript code似乎做你需要什么。这是很慢的,所以你需要单击设置后,要等待。另外,面板的确视图之间不明确,所以你不能告诉任何事情发生,直到窗格的补充。 我只已使用2图像重新present在z方向100像素测试。在功能的getPixels处理此限制所述第一code线,删除一个完整的组图像在z方向。在测试中,我已经进行了相当肤浅的,但似乎通过确定。好了全套的图像。

我想象的3D阵列的一系列三维影像图像(0)在后面运行z方向图像(D-1)在前面。每个图像具有宽度W在y方向上的x方向和高度H。感谢您的挑战,我很喜欢。

链接使用的图像的压缩文件夹是在code端。

 &LT;!DOCTYPE HTML&GT;
&LT; HTML&GT;
&LT; HEAD&GT;
&LT; META HTTP-当量=Content-Type的CONTENT =text / html的;字符集= UTF-8&GT;
&LT;! - 
版权所有(c)2013约翰国王
特此授权,免费的,任何人获得本软件副本及相关文档文件(软件),以处理本软件不受任何限制,包括但不限于使用权,复制,修改,合并,发布,分发,再许可和/或销售软件的副本,并允许个人向谁软件附有这样做,符合以下条件:
上述版权声明和本许可声明应包括在所有副本或实质性部分的软件。
本软件按原样,不附带任何形式,EX $ P $干燥综合征或暗示的,包括但不限于适销性,针对特定用途的适用性和不侵权的保证。在任何情况下,作者或版权所有者都不对任何索赔,损害或其他责任,无论是在一项行动的合同,侵权还是其他原因,从,淘汰或与该软件或使用或其他买卖本软件。
 - &GT;
&LT;冠军&GT;三维切片机&LT; /标题&GT;
&LT;风格类型=文本/ CSS&GT;
    DIV,帆布,IMG {
        位置:绝对的;
    }

    IMG {
        顶部:0px;
        左:0px;
        能见度:隐藏;
    }
    输入{
        文本对齐:右;
    }
    .seen {
        能见度:可见;
    }
    #canvas3D {
        左:10px的;
        顶:10px的;
        能见度:隐藏;
    }
    #canvas2D {
        左:10px的;
        顶:50像素;
        边界:1px的纯黑色;
    }
    #帧 {
        左:650px;
        顶:10px的;
        边界:1px的纯黑色;
        背景颜色:#DDDDDD;
        宽度:600px的;
        高度:600px的;
    }
    #framehead {
        左:0px;
        顶部:0px;
        高度:25像素;
        宽度:100%;
        下边框:1px的纯黑色;
        背景颜色:#999999;
    }
    #用户数据 {
        顶:10px的;
        左:10px的;
    }
    #originins {
        顶:10px的;
        左:10px的;
        宽度:260px;
    }
    #origintext {
        顶:200像素;
        左:10px的;
        宽度:260px;
    }
    #origininput {
        顶:225px;
        左:10px的;
        宽度:260px;
    }
    #originlimits {
        顶:250像素;
        左:10px的;
        宽度:260px;
    }
    #thetaimg {
        顶:10px的;
        左:225px;
    }
    #thetatext {
        顶:200像素;
        左:225px;
        宽度:260px;
    }
    #thetainput {
        顶:225px;
        左:225px;
        宽度:260px;
    }
    #thetalimits {
        顶:250像素;
        左:225px;
        宽度:260px;
    }
    #psiimg {
        顶:10px的;
        左:440px;
    }
    #psitext {
        顶:200像素;
        左:440px;
        宽度:260px;
    }
    #psiinput {
        顶:220px;
        左:440px;
        宽度:260px;
    }
    #psilimits {
        顶:250像素;
        左:440px;
        宽度:260px;
    }
    #setButton {
        顶:310px;
        左:10px的;
        宽度:260px;
    }
    #axes {
        顶:350像素;
        左:10px的;
    }
&LT; /风格&GT;
&LT;脚本类型=文/ JavaScript的&GT;

    //添加一个微调功能,以字符串,如果没有present  - 条从开始和字符串末尾处的空白
    如果(typeof运算String.prototype.trim!=='功能'){
        String.prototype.trim =功能(){
            返回this.replace(/ ^ \ S + | \ s + $ /克,'');
        }
    }

    //缩写功能的getElementById
    功能$(ID){
      返回的document.getElementById(ID);
    }

    //参数为code组象素3D阵列
    变种W = 100; //宽阵列中的x方向的,必须是偶数
    变种D = 100; //数组中的Z方向的深度,必须是偶数
    VAR H = 500; //阵列y方向的高度

    //参数矩形平面PQRS将由通过三维阵列切片选择像素的2D阵列
    // PQRS移动以这样的方式,PQ保持平行于xz​​平面和PS保持平行于yz平面
    //在code设置这些参数
    变种L = 50; //矩形PQ的长度
    变种B = 50; //矩形PS的广度

    //,可以由用户改变的参数初始化。
    变种O =新的点(W / 2,0,D / 2); // O为PQ中间
    变种THETA = 0; //后,飞机绕到O垂直轴角度PQ旋转时,必须-PI / 2与PI / 2之间
    变种磅= 0; //后平面​​绕PQ作为轴角度PS被旋转时,必须介于-PI / 2和PI / 2

    //变量油画
    VAR C3D,C2D;


    / * getPixel得到一个单独的像素从由堆叠的D(深度)形成的像素的2D图像的3D阵列
     *编号从0到D-1,0图像在后面。
     *具有宽度W和高度H的像素的每个图像。
     * 0℃= X&所述; W,0℃= Y&其中; H,0℃= z,其中; d
     *每个图像是在画布上canvas3D
     *
     *此测试img0.jpg将用于img0.jpg到img49.jpg和img50.jpg将用于img50到img99
     * /
    功能与getPixel(的x,y,z)的{
        //下面才行必要的,因为只有两个图像img0.jpg和img50.jpg用于测试
        Z = Math.floor(Z / 50)* 50;
        //上面的行删除,如果用在Z方向全系列图片
        this.ctx.drawImage($(i的+ Z),0,0);
        变种IMDATA = this.ctx.getImageData(0,0,this.width,this.height);
        VAR COL = 4 *(Y * this.width + X);
        VAR PIX =全新的像素();
        pix.red = imdata.data [COL ++];
        pix.green = imdata.data [COL ++];
        pix.blue = imdata.data [COL ++];
        pix.alpha = imdata.data [COL];
        返回PIX;
    }

    //像素对象
    函数像素(){
        this.red;
        this.green;
        this.blue;
        this.alpha;
    }

    //点​​对象
    函数点(X,Y,Z){
        this.x = X;
        this.y = Y;
        this.z = Z;
    }

    功能的Point2D(A,D){
        this.a =一个;
        this.d = D;
    }

    功能setValues​​方法(){
        c2D.ctx.clearRect(0,0,c2D.width,c2D.height);
        VAR Oobj = Ochecked($(OIN)值。);
        如果(!Oobj.OK){
            $(OIN)style.backgroundColor =#F1B7B7。
            返回
        }
        $(OIN)style.backgroundColor =#FFFFFF。
        O = Oobj.point;
        VAR个= parseInt函数($(thetain)value.trim());
        如果(isNaN(次)){
            $(thetain)style.backgroundColor =#F1B7B7。
            返回
        }
        如果(第&其中; =  -  90 ||第i; 90){
            $(thetain)style.backgroundColor =#F1B7B7。
            返回
        }
        $(thetain)style.backgroundColor =#FFFFFF。
        THETA =日* Math.PI / 180;
        变种SI = parseInt函数($(psiin)value.trim());
        如果(isNaN(SI)){
            $(psiin)style.backgroundColor =#F1B7B7。
            返回
        }
        如果(硅&lt; =  -  90 || SI大于90){
            $(psiin)style.backgroundColor =#F1B7B7。
            返回
        }
        $(psiin)style.backgroundColor =#FFFFFF。
        PSI = SI * Math.PI / 180;
        printPane();
    }

    功能Ochecked(Ovalue){
        Ovalue = Ovalue.trim();
        变种V = Ovalue.split(,);
        如果(!V.length = 3){返回【OK:假}};
        变种X = parseInt函数(V [0] .trim());
        变种Y = parseInt函数(V [1] .trim());
        变种Z = parseInt函数(V [2] .trim());
        如果(isNaN(X)|| isNaN(Y)|| isNaN(Z)){返回【OK:假}};
        如果(X&LT; 0 || X  -  GT; = W){返回【OK:假}};
        如果(Y&LT; 0 || Y'的GT = H){返回【OK:假}};
        如果(Z&LT; 0 || Z取代; = D){返回【OK:假}};
        p值=新点(X,Y,Z);
        返回【OK:真的,一点:P};
    }

    功能printPane(){
        VAR P =新的点(OX-Math.round((L / 2)* Math.cos(THETA)),Oy公司,奥兹 -  Math.round((L / 2)* Math.sin(THETA)));
        变种Q =新点(O.x + Math.round((L / 2)* Math.cos(THETA)),Oy公司,盎司+ Math.round((L / 2)* Math.sin(THETA))) ;
        变种S =新点(像素,p.y + Math.round((B)* Math.cos(psi)的),PZ + Math.round((B)* Math.sin(PSI)));
        变种N =新的Point2D(q.x-p.x,q.z-p.z);
        VAR PQincVec = getIncVec(N.A,未检出);
        N =新的Point2D(s.y-p.y,s.z-p.z);
        VAR PSincVec = getIncVec(N.A,未检出);
        VAR像素,COL;
        VAR PSpoint =新的点(p.x,p.y,p.z); //沿着PS点初始化开始为P
        VAR PQpoint; //变量沿着平行于PQ线点
        变种IMDATA = c2D.ctx.getImageData(0,0,c2D.width,c2D.height);
        对于(VAR PS = 0; PS&LT; PSincVec.length; PS ++){
            //增量沿线PS
            PSpoint.y + = PSincVec [PS] .A;
            PSpoint.z + = PSincVec [PS] .D;
            PQpoint =新的点(PSpoint.x,PSpoint.y,PSpoint.z); //沿线平行分PQ初始化到PS上当前点
            对于(VAR PQ = 0; PQ&LT; PQincVec.length; PQ ++){
                //增量沿线PQ
                PQpoint.x + = PQincVec [PQ] .A;
                PQpoint.z + = PQincVec [PQ] .D;
                //检查PQpoint是三维数组中
                如果(0℃= PQpoint.x&安培;&安培; PQpoint.x&所述; W&安培;&安培; 0℃= PQpoint.y&安培;&安培; PQpoint.y&其中;的H&amp;&安培; 0℃= PQpoint.z&安培;&安培; PQpoint.z&LT; D​​){
                    像素= c3D.getPixel(PQpoint.x,PQpoint.y,PQpoint.z);
                    //写沿着线平行点像素PQ到平面
                    COL = 4 *(PS * c2D.width + PQ);
                    imdata.data [COL ++] = pixel.red;
                    imdata.data [COL ++] = pixel.green;
                    imdata.data [COL ++] = pixel.blue;
                    imdata.data [COL] = pixel.alpha;
                }
            }
        }
        c2D.ctx.putImageData(IMDATA,0,0);
    }

    功能getIncVec(A,D){
        变种R,T;
        如果(a取代; Math.abs(d))的{
            变种incVec = getIncs(一个,Math.abs(d))的;
        }
        其他 {
            变种incVec = getIncs(Math.abs(d)中,);
            对于(VAR I = 0; I&LT; incVec.length;我++){
                R = incVec [I];
                T = R.A;
                R.A = r.d;
                r.d = T;
            }
        }
        如果(D&小于0){
            对于(VAR I = 0; I&LT; incVec.length;我++){
                incVec [I] .D * =  -  1;
            }
        }
        返回incVec;
    }

    功能getIncs(A,D){
        VAR P =新的Point2D(0,0);
        VAR VEC = [];
        vec.push(对);
        对于(VAR I = 0; I&LT;一个;我++){
            p值=新的Point2D(1,Math.floor(第(i + 1)* D / A) -  Math.floor(I * D / A));
            vec.push(对);
        }
        返回VEC;
    }

    函数main(){
        //用户输入设置限制和价值观。
        $(OIN)值= O.x +,+ O.y +,+ O.z;
        $(thetain)值= THETA;
        $(psiin)值= PSI;
        $(originlimits)的innerHTML =0安培;其中; = X&安培;其中,+ W +&所述峰; br大于0&安培;其中; = Y&安培;其中,S +&所述峰; br大于0&安培;其中; = Z&放大器; LT;+ D;
        //设置canvas3D使像素可读
        C3D = $(canvas3D);
        c3D.width = W;
        c3D.height = H;
        c3D.ctx = c3D.getContext('二维');
        c3D.getPixel = getPixel;

        //设置canvas2D使得像素硬性
        C2D = $(canvas2D);
        c2D.width = L;
        c2D.height = B;
        c2D.ctx = c2D.getContext('二维');
        c2D.initialise =初始化;

        $(隐藏)style.width = L +PX。
        $(隐藏)style.height = B +PX;
    }
&LT; / SCRIPT&GT;
&LT; /头&GT;
&LT;身体的onload =main()的&GT;
    &LT;! - 图片三维数组列表 - &GT;
    &LT; IMG ID =I0SRC =图像/ img0.jpg&GT;
    &LT; IMG ID =I50SRC =图像/ img50.jpg&GT;
    &LT;! - 图片三维数组列表的末尾 - &GT;

    &LT;帆布ID =canvas3D&GT;&LT; /帆布&GT;
    &LT; D​​IV ID =框架&GT;
        &LT; D​​IV ID =framehead&GT;&安培; NBSP;&安培; NBSP;&安培; NBSP;查看切片窗格&LT的; / DIV&GT;
        &LT;帆布ID =canvas2D&GT;&LT; /帆布&GT;
    &LT; / DIV&GT;
    &LT; D​​IV ID =用户数据&GT;
        &LT; D​​IV ID =originins&GT;输入形式的X,Y,Z&LT; / BR&GT;如40,27,83和LT; / DIV&GT;
        &LT; D​​IV ID =origintext&GT;的位置为原点O&LT; / DIV&GT;
        &LT; D​​IV ID =origininput&GT;&LT;输入ID =OIN&GT;&LT; / DIV&GT;
        &LT; D​​IV ID =originlimits&GT;限制&LT; / DIV&GT;
        &LT; IMG类=看到ID =thetaimgSRC =图像/ theta.png&GT;
        &LT; D​​IV ID =thetatext&GT;西塔度&LT; / DIV&GT;
        &LT; D​​IV ID =thetainput&GT;&LT;输入ID =thetain&GT;&LT; / DIV&GT;
        &LT; D​​IV ID =thetalimits&GT; -90安培; LT; THETA和放大器; LT; = 90°; / DIV&GT;
        &LT; IMG类=看到ID =psiimgSRC =图像/ psi.jpg&GT;
        &LT; D​​IV ID =psitext&GT;幽度&LT; / DIV&GT;
        &LT; D​​IV ID =psiinput&GT;&LT;输入ID =psiin&GT;&LT; / DIV&GT;
        &LT; D​​IV ID =psilimits&GT; -90安培; LT; PSI和放大器; LT; = 90°; / DIV&GT;
        &LT; D​​IV ID =SET按钮&GT;&LT;输入类型=按钮值=SET的onclick =setValues​​方法()「&GT;&LT; / DIV&GT;
        &LT; IMG类=看到ID =轴SRC =图像/ axes.jpg&GT;
    &LT; / DIV&GT;
&LT; D​​IV ID =味精&GT;&LT; / DIV&GT;
&LT; /身体GT;
&LT; / HTML&GT;
 

<一个href="https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxqa2dhZGdldHN0b3JlfGd4Ojc5YWNiYjE3MjNmZGE5NjM"相对=nofollow>在code 使用的图像

I'm trying to make realtime mock-ultrasound images from volumetric CT data. The trick is that the user controls the position of the probe, which defines the plane they are seeing.

What I've done so far is read the pixel data from all the dicom images into a single 3D array of pixels, and now what I need to do is reslice that 3D array at different angles. Sorry if the following description comes off a little sloppy, but imagine a 3D rectangular box (say 100 pixels wide and deep [x,z], and 500 long [y]) and a 2D "viewing plane" (say 50 x 50 pixels). Say the starting position of the viewing plane (origin defined as the middle point at the close edge of the plane - [0,25]) is with the origin at [50,250,0] (dead center of the top surface, looking down), oriented left to right and piercing the rectangle straight down. Thus, the viewing plane has three parameters that can be changed - the location of the origin, the rotation around the vertical (the line running from the origin to the corresponding point on the opposite edge of the plane), and the "tilt" (rotation of the plane around the line where it intersects with the box). So the user can change those three parameters, and the output is an image built from the pixels "touched" by the viewing plane.

Again, I apologize if the description is sloppy, but I'm a med student without a strong mathematics background. Any help would be greatly appreciated.

解决方案

I would write the 2D equation for a line, solve for each value of x, and round the resulting y variable to the nearest integer– Edje09 yesterday

Sticking to the 2D case for the moment the method you suggest has two main issues

  1. If the line is steeper than gradient of 1 then some pixels can be missed.
  2. rounding can choose a pixel above the one you would want to choose.

This pdf shows the issues and a possible solution for the 2D case which can then be built on for the 3D case.

EDIT After further thought I may have produced an written pdf outline solution for the 3D case that can be turned into an algorithm and hence into code. This is as far as I have got I have done no checking and cannot guarentee its correctness but hopefully will take you a stage further.

EDIT CODE ADDED The following Javascript code seems to do what you require. It is quite slow so you need to wait after clicking SET. Also the 'pane' does not clear between views so you cannot tell anything is happening until the 'pane' is refilled. I have only tested by using 2 images to represent 100 pixels in the z direction. The first code line in the function getPixels deals with this limitation, remove for a full set of images in the z direction. The test I have carried out are fairly superficial but seem to pass OK. Better with a full set of images.

I have imagined the 3D array as a series of D images image(0) at the back running the z direction to image(D-1) at the front. Each image having width W in the x direction and height H in the y direction. Thanks for the challenge I enjoyed it.

Links to a zipped folder of images used is at end of code.

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--
Copyright (c)  2013   John King
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-->
<title>3D Slicer</title>
<style type="text/css">
    div, canvas, img {
        position: absolute;
    }

    img {
        top:0px;
        left:0px;
        visibility:hidden;
    }
    input {
        text-align: right;
    }
    .seen {
        visibility: visible;
    }   
    #canvas3D {
        left:10px;
        top:10px;
        visibility:hidden;
    }
    #canvas2D {
        left:10px;
        top:50px;
        border:1px solid black;
    }
    #frame {
        left:650px;
        top:10px;
        border:1px solid black;
        background-color: #DDDDDD;
        width:600px;
        height:600px;
    }
    #framehead {
        left:0px;
        top:0px;
        height:25px;
        width:100%;
        border-bottom: 1px solid black;
        background-color: #999999;
    }
    #userdata {
        top:10px;
        left:10px;
    }
    #originins {
        top:10px;
        left:10px;
        width:260px;
    }
    #origintext {
        top:200px;
        left:10px;
        width:260px;
    }
    #origininput {
        top:225px;
        left:10px;
        width:260px;        
    }
    #originlimits {
        top:250px;
        left:10px;
        width:260px;
    }
    #thetaimg {
        top:10px;
        left:225px;
    }
    #thetatext {
        top:200px;
        left:225px;
        width:260px;
    }
    #thetainput {
        top:225px;
        left:225px;
        width:260px;
    }
    #thetalimits {
        top:250px;
        left:225px;
        width:260px;
    }
    #psiimg {
        top:10px;
        left:440px;
    }
    #psitext {
        top:200px;
        left:440px;
        width:260px;
    }
    #psiinput {
        top:220px;
        left:440px;
        width:260px;
    }
    #psilimits {
        top:250px;
        left:440px;
        width:260px;
    }
    #setButton {
        top:310px;
        left:10px;
        width:260px;
    }
    #axes {
        top:350px;
        left:10px;
    }
</style>
<script type="text/javascript">

    //add a trim function to string if not present - strips white space from start and end of string
    if(typeof String.prototype.trim !== 'function') {
        String.prototype.trim = function() {
            return this.replace(/^\s+|\s+$/g, ''); 
        }
    }

    // abbreviation function for getElementById
    function $(id) {
      return document.getElementById(id);
    }

    //parameters for 3D array of pixels set in code
    var W=100; //width of array in x direction, must be even
    var D=100; //depth of array in z direction, must be even
    var H=500; //height of array in y direction

    //parameters for the rectangular plane PQRS that will select the pixels for a 2D array by slicing through the 3D array
    //PQRS moves in such a way that PQ remains parallel to xz plane and PS remains parallel to yz plane
    //these parameters set in code
    var L=50; //length of rectangle PQ
    var B=50; //breadth of rectangle PS

    //Initialisation of parameters that can be changed by the user.
    var O=new Point(W/2,0,D/2); //O is middle of PQ
    var theta=0; //angle PQ is rotated after plane is rotated about a vertical axis through O, must be between -PI/2 and PI/2
    var psi=0; //angle PS is rotated after plane is rotated about PQ as an axis, must be between -PI/2 and PI/2

    //variable for canvases
    var c3D, c2D;


    /*getPixel gets an individual pixel from the 3D array of pixels formed by a stack of D (for depth) 2D images
     * numbered from 0 to D-1, with 0 being the image at the back.
     * Each image having width W and height H pixels.
     * 0<= x <W, 0<= y <H,  0<= z <D
     * each image is on the canvas canvas3D
     * 
     * for this test img0.jpg will be used for img0.jpg to img49.jpg  and img50.jpg will be used for img50 to img99
     */
    function getPixel(x,y,z) {
        // line below only required because just two images img0.jpg and img50.jpg are used for testing
        z=Math.floor(z/50)*50;          
        //Remove above line if full series of images used in z direction
        this.ctx.drawImage($("i"+z),0,0);
        var imdata=this.ctx.getImageData(0,0,this.width,this.height);
        var col=4*(y*this.width+x);
        var pix=new Pixel();
        pix.red=imdata.data[col++];
        pix.green=imdata.data[col++];
        pix.blue=imdata.data[col++];
        pix.alpha=imdata.data[col];
        return pix;
    }

    //Pixel Object
    function Pixel() {
        this.red;
        this.green;
        this.blue;
        this.alpha;
    }

    //Point Object
    function Point(x,y,z) {
        this.x=x;
        this.y=y;
        this.z=z;
    }

    function Point2D(a,d) {
        this.a=a;
        this.d=d;
    }

    function setValues() {
        c2D.ctx.clearRect(0,0,c2D.width,c2D.height);
        var Oobj=Ochecked($("Oin").value);
        if(!Oobj.OK) {
            $("Oin").style.backgroundColor="#F1B7B7";
            return
        }
        $("Oin").style.backgroundColor="#FFFFFF";
        O=Oobj.point;
        var th=parseInt($("thetain").value.trim());
        if(isNaN(th)) {
            $("thetain").style.backgroundColor="#F1B7B7";
            return
        }
        if(th<=-90 || th>90) {
            $("thetain").style.backgroundColor="#F1B7B7";
            return
        }
        $("thetain").style.backgroundColor="#FFFFFF";
        theta=th*Math.PI/180;
        var si=parseInt($("psiin").value.trim());
        if(isNaN(si)) {
            $("psiin").style.backgroundColor="#F1B7B7";
            return
        }
        if(si<=-90 || si>90) {
            $("psiin").style.backgroundColor="#F1B7B7";
            return
        }
        $("psiin").style.backgroundColor="#FFFFFF";
        psi=si*Math.PI/180;
        printPane();
    }

    function Ochecked(Ovalue) {
        Ovalue=Ovalue.trim();
        var V=Ovalue.split(",");
        if(V.length!=3) {return {OK:false}};
        var x=parseInt(V[0].trim());
        var y=parseInt(V[1].trim());
        var z=parseInt(V[2].trim());
        if(isNaN(x) || isNaN(y) || isNaN(z))  {return {OK:false}};
        if(x<0 || x>=W) {return {OK:false}};
        if(y<0 || y>=H) {return {OK:false}};
        if(z<0 || z>=D) {return {OK:false}};
        p=new Point(x,y,z);
        return {OK:true,point:p};
    }

    function printPane(){
        var p = new Point(O.x-Math.round((L/2)*Math.cos(theta)),O.y,O.z - Math.round((L/2)*Math.sin(theta)));
        var q = new Point(O.x+Math.round((L/2)*Math.cos(theta)),O.y,O.z + Math.round((L/2)*Math.sin(theta)));
        var s = new Point(p.x,p.y+Math.round((B)*Math.cos(psi)),p.z + Math.round((B)*Math.sin(psi)));
        var n = new Point2D(q.x-p.x,q.z-p.z);       
        var PQincVec=getIncVec(n.a,n.d);
        n = new Point2D(s.y-p.y,s.z-p.z);       
        var PSincVec=getIncVec(n.a,n.d);
        var pixel,col;
        var PSpoint =new Point(p.x,p.y,p.z); // points along PS initialised to start at P
        var PQpoint; //variable for points along line parallel to PQ
        var imdata=c2D.ctx.getImageData(0,0,c2D.width,c2D.height);          
        for(var ps=0;ps<PSincVec.length;ps++) {
            //increment along line PS
            PSpoint.y+=PSincVec[ps].a;
            PSpoint.z+=PSincVec[ps].d;
            PQpoint =new Point(PSpoint.x,PSpoint.y,PSpoint.z); // points along line parallel to PQ initialised to current point on PS
            for(var pq=0;pq<PQincVec.length;pq++) {
                //increment along line PQ
                PQpoint.x+=PQincVec[pq].a;
                PQpoint.z+=PQincVec[pq].d;
                //check that PQpoint is inside 3D array
                if(0<=PQpoint.x && PQpoint.x<W && 0<=PQpoint.y && PQpoint.y<H && 0<=PQpoint.z && PQpoint.z<D) {
                    pixel=c3D.getPixel(PQpoint.x,PQpoint.y,PQpoint.z);
                    //write pixel from point along line parallel to PQ onto plane
                    col=4*(ps*c2D.width+pq);
                    imdata.data[col++]=pixel.red;
                    imdata.data[col++]=pixel.green;
                    imdata.data[col++]=pixel.blue;
                    imdata.data[col]=pixel.alpha;
                }               
            }
        }
        c2D.ctx.putImageData(imdata,0,0);
    }

    function getIncVec(a,d) {
        var r,t;
        if(a>Math.abs(d)) {
            var incVec=getIncs(a,Math.abs(d));
        }
        else {
            var incVec=getIncs(Math.abs(d),a);
            for(var i=0;i<incVec.length;i++) {
                r=incVec[i];
                t=r.a;
                r.a=r.d;
                r.d=t;
            }
        }
        if(d<0) {
            for(var i=0;i<incVec.length;i++) {
                incVec[i].d*=-1;
            }
        }
        return incVec;
    }

    function getIncs(a,d) {
        var p=new Point2D(0,0);
        var vec=[];
        vec.push(p);
        for(var i=0;i<a;i++) {
            p=new Point2D(1,Math.floor((i+1)*d/a) - Math.floor(i*d/a));
            vec.push(p);
        }
        return vec;
    }

    function main() {
        //set limits and values for user input.
        $("Oin").value=O.x+","+O.y+","+O.z;
        $("thetain").value=theta;
        $("psiin").value=psi;
        $("originlimits").innerHTML="0&lt;= x &lt;"+W+"<br>0&lt;= y &lt;"+H+"<br>0&lt;= z &lt;"+D;
        //set canvas3D so that pixels are readable
        c3D=$("canvas3D");
        c3D.width=W;
        c3D.height=H;
        c3D.ctx=c3D.getContext('2d');
        c3D.getPixel=getPixel;

        //set canvas2D so that pixels are settable
        c2D=$("canvas2D");
        c2D.width=L;
        c2D.height=B;
        c2D.ctx=c2D.getContext('2d');
        c2D.initialise=initialise;

        $("hide").style.width=L+"px";
        $("hide").style.height=B+"px";
    }
</script>
</head>
<body onload="main()">
    <!-- list of images for 3D array -->
    <img id="i0" src="images/img0.jpg">
    <img id="i50" src="images/img50.jpg">
    <!-- end of list of images for 3D array -->

    <canvas id="canvas3D"></canvas>
    <div id="frame">
        <div id="framehead">&nbsp;&nbsp;&nbsp;View of Slicing Pane</div>
        <canvas id="canvas2D"></canvas>
    </div>
    <div id="userdata">
        <div id="originins">Enter in form x,y,z </br> eg 40,27,83</div>
        <div id="origintext">Position for Origin O</div>
        <div id="origininput"><input id="Oin"></div>
        <div id="originlimits">limits</div>
        <img class="seen" id="thetaimg" src="images/theta.png">
        <div id="thetatext">Theta in degrees</div>
        <div id="thetainput"><input id="thetain"></div>
        <div id="thetalimits">-90 &lt; theta &lt;=90</div>
        <img class="seen" id="psiimg" src="images/psi.jpg">
        <div id="psitext">Psi in degrees</div>
        <div id="psiinput"><input id="psiin"></div>
        <div id="psilimits">-90 &lt; psi &lt;=90</div>
        <div id="setButton"><input type="button" value="SET" onclick="setValues()"></div>
        <img class="seen" id="axes" src="images/axes.jpg">
    </div>
<div id="msg"></div>    
</body>
</html>

images used in code

这篇关于一种制造三维阵列的2D横截面切片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆