การสร้างภาพเมฆแบบชี้ด้วย Three.js

Nov 28 2022
คุณรู้วิธีบรรลุความสมบูรณ์แบบหรือไม่? เพียงรวม Midjourney, Stable Diffusion, แผนที่เชิงลึก, การปรับรูปร่างใบหน้า และ 3D เล็กน้อย
ฉันได้ทำงานในโปรเจ็กต์ภายในมาระยะหนึ่งแล้ว ซึ่งเอาต์พุตเหนือสิ่งอื่นใดคือพอยต์คลาวด์เช่นกัน ตอนที่ฉันคิดเกี่ยวกับเครื่องมือสร้างภาพที่เหมาะสมของคลาวด์เหล่านั้น ฉันทำรายการสิ่งที่ควรจะทำได้: เช่นเคย โปรเจ็กต์เสริมนี้ไม่ได้จบแค่การสร้างภาพเท่านั้น
บ้านในจุดเมฆ

ฉันได้ทำงานในโปรเจ็กต์ภายในมาระยะหนึ่งแล้ว ซึ่งเอาต์พุตเหนือสิ่งอื่นใดคือพอยต์คลาวด์เช่นกัน เมื่อฉันคิดเกี่ยวกับเครื่องมือสร้างภาพที่เหมาะสมของเมฆเหล่านั้น ฉันทำรายการสิ่งที่ควรจะทำได้:

  • การปรับแต่ง
  • ความสามารถในการส่งผ่านจุดสุดยอด/ส่วนแยกส่วนที่กำหนดเอง
  • ง่ายต่อการแบ่งปัน

เช่นเคย โครงการเสริมนี้ไม่ได้จบเพียงแค่การแสดงภาพเท่านั้น เนื่องจากฉันต้องการทำให้สิ่งนี้น่าสนใจสำหรับคนอื่นๆ ด้วย ในอีก 15 นาทีข้างหน้า ฉันจะบอกคุณบางอย่างเกี่ยวกับแผนที่เชิงลึก / MiDaS, การจดจำใบหน้า / TensorFlow, อัลกอริทึม Beier–Neely morphing และเอฟเฟกต์พารัลแลกซ์ ไม่ต้องกังวล จะมีรูปภาพและวิดีโอมากมายเช่นกัน โพสต์นี้ไม่ใช่บทช่วยสอนโดยละเอียด แต่เป็นการนำเสนอแนวคิดบางส่วน อย่างไรก็ตาม มันควรจะดีพอสำหรับเป็นสื่อการเรียนรู้ร่วมกับที่เก็บ Github

เกี่ยวกับ point clouds และ Three.js

โดยทั่วไปแล้ว point cloud เป็นเพียงชุดของจุดในอวกาศ สามารถรับเมฆจุดดังกล่าวได้ผ่านเช่น Lidars หรือแม้กระทั่งผ่าน iPhone รุ่นใหม่ Top Lidar สามารถสร้างจุดได้หลายล้านจุดต่อวินาที ดังนั้นการเพิ่มประสิทธิภาพและการใช้งาน GPU จึงเป็นสิ่งจำเป็นสำหรับการแสดงภาพที่เหมาะสม ฉันอาจเขียนโพสต์อื่นเกี่ยวกับวิธีการสร้างภาพด้วยความเคารพต่อขนาดชุดข้อมูล แต่สำหรับตอนนี้ Three.js และ WebGL นั้นดีพอ หวังว่าเร็ว ๆ นี้เราจะสามารถใช้WebGPUได้เช่นกัน — มีให้ใช้งานแล้ว แต่ยังไม่เป็นทางการในแง่ของ Three.js

Three.js ให้วัตถุ Points 3D ที่แสดงผลภายในด้วยแฟ ล็ กgl.POINTS ระดับคะแนนต้องการสองพารามิเตอร์ วัสดุ และเรขาคณิต มาดูกันดีกว่าว่าทั้งคู่

เรขาคณิต

มีรูปทรงเรขาคณิตตัวช่วยมากมาย เช่น Plane of Sphere ที่ให้คุณสร้างต้นแบบได้เร็วขึ้น แต่ฉันตัดสินใจใช้BufferGeometry แม้ว่าคุณจะใช้เวลามากขึ้นในการสร้างบางสิ่งที่มีความหมาย แต่คุณก็สามารถควบคุมทุกอย่างที่เกี่ยวข้องกับรูปทรงเรขาคณิตได้อย่างเต็มที่ ลองดูตัวอย่างด้านล่าง:

if (this.guiWrapper.textureOptions.textureUsage) {
  geometry.setAttribute('uv', new THREE.BufferAttribute(pointUv, this.UV_NUM))
} else {
  geometry.setAttribute('color', new THREE.BufferAttribute(pointsColors, this.COL_NUM))
}
geometry.setAttribute('position', new THREE.BufferAttribute(pointsToRender, this.POS_NUM))

geometry.morphAttributes.position = []
geometry.morphAttributes.color = []

// Add flat mapping
geometry.morphAttributes.position[0] = new THREE.BufferAttribute(pointsToRenderFlat, this.POS_NUM)
geometry.morphAttributes.color[0] = new THREE.BufferAttribute(pointsColors, this.COL_NUM)

// Add "natural" mapping
geometry.morphAttributes.position[1] = new THREE.BufferAttribute(pointsToRenderMapped, this.POS_NUM)
geometry.morphAttributes.color[1] = new THREE.BufferAttribute(pointsColors, this.COL_NUM)

ในกรณีง่ายๆ เช่น ภาพ 2 มิติ คุณสามารถใช้แผนที่ความลึกเป็นแผนที่การเคลื่อนที่ แต่ในกรณีการใช้งานของฉัน ฉันต้องการลักษณะการทำงานที่ซับซ้อนมากขึ้นของพอยต์คลาวด์และโมเดล 3 มิติ

วัสดุ

อีกครั้ง มีวัสดุที่กำหนดไว้ล่วงหน้าจำนวนมากใน Three.js แต่ในกรณีของเรา ฉันใช้ShaderMaterialเนื่องจากเราสามารถจัดหาเฉดสีที่กำหนดเองได้และมีความยืดหยุ่นมากขึ้น / ประสิทธิภาพที่ดีขึ้น ตัวอย่างถัดไปแสดงทั้งจุดสุดยอดพื้นฐานและตัวแบ่งส่วน บางส่วนรวมถึง Three.js เฉพาะ ดังนั้นคุณจึงสามารถใช้ประโยชน์จาก API ที่เข้าถึงได้จากฝั่ง TypeScript เนื่องจากรหัสของพวกเขามีอยู่ในGithubคุณจึงสามารถตรวจสอบสิ่งที่จำเป็นได้ตลอดเวลา

export const VERTEX_SHADER = `
    #ifdef GL_ES
    precision highp float;
    #endif

    uniform float size;
    uniform float scale;
    #define USE_COLOR
    #define USE_MORPHCOLORS
    #include <common>
    #include <color_pars_vertex>
    #include <morphtarget_pars_vertex>
    
    attribute vec3 color;
    varying vec2 vUv;
    uniform float u_time;
    uniform float u_minZ;
    uniform float u_maxZ;
    uniform float u_scale;
    uniform vec3 u_camera_angle;
    uniform int u_parallax_type;
    
    void main()
    {
        #include <color_vertex>
        #include <begin_vertex>
        #include <morphtarget_vertex>
        vColor = color;
        vUv = uv;
        gl_PointSize = 2.0;

        #if defined( MORPHTARGETS_COUNT )
            vColor = morphTargetBaseInfluence;
            for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {
                if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb morphTargetInfluences[ i ];
            }
        #endif

        if (u_parallax_type == 1) {
            transformed.x += u_scale*u_camera_angle.x*transformed.z*0.05;
            transformed.y += u_scale*u_camera_angle.y*transformed.z*0.02;
        } else if (u_parallax_type == 2) {
            transformed.x += transformed.z*cos(0.5*u_time)*0.01;
            transformed.y += transformed.z*sin(0.5*u_time)*0.005;
        }

        vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );
        gl_Position = projectionMatrix * mvPosition;
    }
`;

export const FRAGMENT_SHADER = `
    #ifdef GL_ES
    precision highp float;
    #endif

    varying vec3 vColor;
    varying vec2 vUv;
    uniform sampler2D u_texture;
    uniform bool u_use_texture;

    void main()
    {
        if (u_use_texture) {
            gl_FragColor = texture2D(u_texture, vUv);
        } else {
            gl_FragColor = vec4( vColor, 1.0 );
        }
    }
    
`;

หมายเหตุสำคัญเกี่ยวกับเว็บไซต์สาธิตสด Gif และวิดีโอที่เห็นในโพสต์นี้สร้างขึ้นในขณะที่ Visualizer ใช้ทรัพยากรดิบ / HQ การสาธิตสดใช้สินทรัพย์ที่ลดขนาดลงโดยประมาณ 10 เท่า รวมทั้งหมดประมาณ 15MB นอกจากนี้ ในขณะที่ใช้ env ในเครื่อง ฉันสามารถโหลดข้อมูลดิบและรอสักครู่ การสาธิตสดกำลังใช้ทางลัดภายในเพื่อทำให้ประสบการณ์ดีขึ้น กล่าวคือ โครงสร้างรูปทรงเรขาคณิตถูกโหลดบางส่วนไปยังผู้ปฏิบัติงานเว็บหรือโหลดเนื้อหาเป็นพื้นผิว และโหลดลงในผ้าใบนอกหน้าจอ ดังนั้นฉันจึงสามารถอ่าน ImageData และเติมบัฟเฟอร์พื้นฐานและมอร์ฟิงได้ — เวลาที่สิ้นหวังเรียกร้องมาตรการที่สิ้นหวัง :]

มาทำให้เมฆเหล่านั้นเต้นรำกันเถอะ!

ในสองสามส่วนถัดไป ฉันจะบอกคุณเพิ่มเติมเกี่ยวกับแนวคิดที่ใช้แล้วและอธิบายสถานการณ์จำลองที่คุณสามารถลองได้ในภายหลัง ฉันจะไม่อธิบายไปป์ไลน์อย่างเช่น [ชุดรูปภาพ -> การจดจำใบหน้า -> การส่งออกข้อมูล -> การผสานรวม Three.js] โดยละเอียด แต่คุณควรทำด้วยตัวเองโดยทำตามลิงก์ที่ให้ไว้

มองตาฉันสิ — ขอบคุณ Ryska

MiDaS และเอฟเฟกต์พารัลแลกซ์

MiDaSเป็นโมเดลแมชชีนเลิร์นนิงที่น่าทึ่งที่สามารถคำนวณแผนที่ความลึกจากภาพเดียว (การประมาณความลึกด้วยตาข้างเดียว) เรียนรู้จากชุดข้อมูลหลายชุด ให้ผลลัพธ์ที่ดีมาก และคุณมีอิสระที่จะใช้มันบนอุปกรณ์ของคุณ การตั้งค่าส่วนใหญ่ทำผ่าน conda คุณเพียงแค่ต้องดาวน์โหลดรุ่นที่ต้องการ (สูงสุด 1.3GB) ช่างเป็นเวลาที่มีชีวิตชีวา คุณสามารถใช้ประโยชน์จากความรู้ของกลุ่มคนฉลาดๆ ที่บ้านได้อย่างง่ายดาย เมื่อคุณมีรูปภาพและแผนที่ความลึกที่เกี่ยวข้องแล้ว ไม่มีอะไรหยุดคุณจากการใช้สิ่งนั้นใน point cloud renderer ดังที่คุณเห็นในวิดีโอถัดไป

แต่เดี๋ยวก่อนยังมีอีก! โมเดล Text2Image, Image2Image, Text2Mesh ฯลฯ กำลังเป็นที่นิยมและเราไม่สามารถอยู่ข้างหลังได้ วิดีโอถัดไปแสดงภาพที่สร้างโดย AI ไม่กี่ภาพ (Midjourney และ Stable Diffusion) ที่ประมวลผลผ่าน MiDaS และแสดงภาพ (เมื่อภาพมีการเปลี่ยนแปลง เราจะเปลี่ยนเฉพาะบัฟเฟอร์ morphing ที่กล่าวถึงก่อนหน้านี้เท่านั้น):

คุณอาจตระหนักว่าภาพกำลังเคลื่อนไหว นั่นคืองานของจุดสุดยอด Shader ของเรา เอฟเฟกต์พารัลแลกซ์มีสองประเภท อันแรกกำลังติดตามตำแหน่งของกล้องและพยายามปรับเปลี่ยนก้อนเมฆเพื่อให้ศูนย์กลางของภาพจ้องไปที่กล้องโดยตรง เอฟเฟ็กต์นี้ใช้งานได้ดี แต่ควรมีความซับซ้อนมากกว่านี้ เนื่องจากแรงโน้มถ่วงไม่ได้อยู่ที่จุดศูนย์กลางเสมอไป ฉันวางแผนที่จะเขียนแนวทางที่ดีขึ้นในเร็ว ๆ นี้ ข้อมูลโค้ดด้านล่างแสดงการส่งผ่านพารัลแลกซ์พารัมไปยังเครื่องแบบ เพื่อให้ทุกจุดยอดสามารถปรับตามกันได้ ในที่สุดสิ่งนี้จะถูกย้ายไปที่ shaders เช่นกัน

//Will be simplified once testing / prototyping is finished
const imgCenterPoint = this.geometryHelper.imgCenterPoints[this.guiWrapper.renderingType.getValue()][this.interpolationOptions.frame]
this.lastCameraPosition = this.camera.position.clone()
let angleX = this.camera.position.clone().setY(0).angleTo(imgCenterPoint)
let angleY = this.camera.position.clone().setX(0).angleTo(imgCenterPoint)

let normalX = new THREE.Plane().setFromCoplanarPoints(new THREE.Vector3(), this.camera.position.clone().setY(0), imgCenterPoint).normal
let normalY = new THREE.Plane().setFromCoplanarPoints(new THREE.Vector3(), this.camera.position.clone().setX(0), imgCenterPoint).normal

this.parallaxUniforms.u_scale.value = 1 + this.currentSceneObject.scale.z
this.parallaxUniforms.u_camera_angle.value = new THREE.Vector3(-angleX*normalX.y, angleY*normalY.x, 0)

การแปลงภาพและหมีโคอาล่า

แผนที่เชิงลึกและรูปภาพที่สร้างโดย AI นั้นยอดเยี่ยมและทั้งหมด แต่เราต้องให้ความเคารพต่อธรรมชาติด้วย มีสถานการณ์ชื่อ Koala มีสองลักษณะ การแปลงภาพระหว่างสองภาพและการแสดงภาพ "ธรรมชาติ" เรามาเริ่มกันที่หลัง ในสถานการณ์สมมตินี้ สีจะถูกแมปลงในระนาบ z เป็นตัวเลข 24 บิต แล้วทำให้เป็นมาตรฐาน มีบางอย่างที่มหัศจรรย์เกี่ยวกับการแสดงภาพเชิงพื้นที่ โดยเฉพาะอย่างยิ่งเพราะมันทำให้เรามีโอกาสรู้สึกถึงวัตถุและยังให้สัญชาตญาณที่ใกล้เคียงยิ่งขึ้นว่าเหตุใดเครือข่ายประสาทสำหรับการจดจำวัตถุจึงทำงานได้ คุณเกือบจะรู้สึกได้ถึงลักษณะของวัตถุในอวกาศ:

เกี่ยวกับการ morphing ในสถานการณ์นี้เรามีโคอาล่าที่แตกต่างกันสองตัวและรูปภาพระดับกลางที่ทำการ morphing 10 รูป — คุณสามารถเรียกดูผ่านตัวเลื่อนหรือปล่อยให้ WebGL เคลื่อนไหวในลักษณะที่เด้งได้ รูปภาพระหว่างต้นฉบับถูกสร้างขึ้นด้วยอัลกอริทึมการแปรอักษร Beier–Neely สำหรับอัลกอริทึมนี้ คุณต้องแมปเส้น 1-1 ชุด โดยแต่ละเส้นจะแสดงถึงคุณลักษณะบางอย่าง เช่น ดวงตาหรือคาง ฉันจะพูดถึงอัลกอริทึมโดยละเอียดในหัวข้อถัดไป ในวิดีโอด้านล่าง คุณจะเห็นว่ามีชิ้นส่วนแปลก ๆ เนื่องจากฉันใช้ฟีเจอร์บรรทัดเพียง 10 รายการและต้องการดูผลลัพธ์ แต่ถึงแม้จะไม่สมบูรณ์แบบ เวอร์ชันแอนิเมชันก็ค่อนข้างดี โดยเฉพาะผ้าพันคอที่ปรับเปลี่ยนได้ ซึ่งทำให้เคลิบเคลิ้มมาก:

Image Morphing, Portrait Depth API และ waifus

ถึงเวลาที่จะรวมทุกอย่างเข้าด้วยกันและดูตัวอย่างที่ซับซ้อนมากขึ้น ในส่วนนี้ เราจะสอดแทรกระหว่างภาพสองภาพด้วยแผนที่ความลึก ดังที่ฉันได้กล่าวไว้ โดยทั่วไปแล้ว MiDaS นั้นยอดเยี่ยมมาก แต่เมื่อคุณต้องการแผนที่ความลึกของใบหน้ามนุษย์ คุณต้องใช้โมเดลที่มีรายละเอียดมากขึ้น ซึ่งได้รับการฝึกฝนมาเป็นพิเศษสำหรับใบหน้ามนุษย์ (นั่นไม่ได้หมายความว่าคุณไม่สามารถรับแผนที่ความลึกที่ดีพอจาก มิดาส). เมื่อต้นปีนี้ TensorFlow ได้นำเสนอPortrait Depth APIและคล้ายกับ MiDaS โครงการนี้ยังสามารถรันบนอุปกรณ์ของคุณได้อีกด้วย สำหรับส่วนนี้ ฉันเลือกแอนน์ แฮทธาเวย์และโอลิเวีย มันน์เป็นเป้าหมายที่ปรับเปลี่ยน ในเมนูแบบเลื่อนลง คุณจะพบส่วนนี้ชื่อว่า Waifus อย่าถามฉันว่าทำไม ก่อนแปลงร่าง เรามาดูแผนที่ความลึกของแอนน์ในฐานะพอยต์คลาวด์กันก่อน:

เราจะเห็นว่าโมเดลให้ผลลัพธ์ที่ค่อนข้างดี เราไม่สามารถสร้างตาข่ายที่เหมือนจริงของใบหน้ามนุษย์ได้ แต่อย่างอื่นก็มีรายละเอียดที่ดี เช่น ฟันที่มีวอลลุ่ม ศีรษะในระดับที่แตกต่างจากลำตัวอย่างชัดเจน และการบรรเทาใบหน้าก็แม่นยำเช่นกัน อาจกล่าวได้ว่ามีความผิดพลาดของภาพเพราะดูเหมือนว่าแอนมีคางที่สอง แต่เรารู้ว่ามันไม่เป็นความจริง ดังที่ Nietzsche กล่าวว่า "เฉพาะผู้ที่มีคางสองชั้นเท่านั้นที่มองลงไปยังก้นบึ้งแห่งชีวิตที่ล้มเหลวของพวกเขา" นอกจากนี้ อย่าเชื่อถือคำพูดใด ๆ บนอินเทอร์เน็ต :]

Portrait Depth API ทำงานในสองขั้นตอน ในตอนแรกจะตรวจจับใบหน้า/ขน/คอและส่วนอื่นๆ ที่อยู่ใกล้ใบหน้า (ฉันไม่แน่ใจว่าน้ำหนักนี้เป็นอย่างไร ประโยคนี้มาจากประสบการณ์ของฉันขณะยุ่งกับโมเดลเท่านั้น) จากนั้นมันก็สร้างหน้ากากสีดำลงบน ภาพที่เหลือและสร้างแผนที่ความลึกในที่สุด ขั้นตอนการกำบังไม่แม่นยำเสมอไป ดังนั้นแผนที่ความลึกสุดท้ายจึงมีสัญญาณรบกวนที่ขอบ โชคดีที่มันเป็นสัญญาณรบกวนที่ดี — สามารถลบออกได้ในคำแนะนำแบบเดียวกันในขณะที่เขียนไปยังบัฟเฟอร์ ฉันเขียนฮิวริสติกเพื่อลบเสียงรบกวนและใช้งานได้เมื่อลองครั้งแรก

removeNoise(depthMapColor: number, depthmap: Array<Array<number>>, i: number, j: number, width: number, height: number, pointPointer: number, pointsToRender: Float32Array) {
        if (depthMapColor != 0) {
            const percentage = depthMapColor/100
            let left = depthMapColor
            let right = depthMapColor
            let top = depthMapColor
            let down = depthMapColor
            const dropThreshold = 5*percentage
    
            if (j > 0) left = depthmap[i][j-1]
            if (j < width-1) right = depthmap[i][j+1]
            if (i > 0) top = depthmap[i-1][j]
            if (i < height-1) down = depthmap[i+1][j]
            
            if(Math.abs(left - depthMapColor) > dropThreshold || Math.abs(right - depthMapColor) > dropThreshold) {
                pointsToRender[pointPointer*3 + 2] = 0
            }
            else if(Math.abs(top - depthMapColor) > dropThreshold || Math.abs(down - depthMapColor) > dropThreshold) {
                pointsToRender[pointPointer*3 + 2] = 0
            } else {
                // extra scale
                pointsToRender[pointPointer*3 + 2] = 3*(1 - depthMapColor)
            }
        }
    }

ก่อนและหลังการดีนอยส์

เนื่องจากการดัดแปลงโคอาล่าทำให้เกิดชิ้นส่วน ฉันตัดสินใจใช้ TensorFlow และโมเดลการจดจำใบหน้า/คุณลักษณะของพวกมันสำหรับการดัดแปลงใบหน้า ฉันใช้ฟีเจอร์ไลน์ประมาณ 80 เส้น มากกว่าโคอาล่า 8 เท่า:

ผลลัพธ์ที่ได้นั้นดีกว่ามากและดูเจ๋งมาก แม้ว่าจะใช้แผนที่เชิงลึกก็ตาม! มีข้อผิดพลาดอย่างหนึ่งกับเส้นผมของ Olivia แต่ไม่มีอะไรที่แก้ไขไม่ได้ในภายหลัง เมื่อคุณทำให้ morphing นี้เคลื่อนไหว แต่ละเฟรมจะตัดพิกเซลออก นี่เป็นเพราะพื้นหลังถือว่าแบนแต่สามารถแก้ไขได้ในขั้นตอนหลังการประมวลผล เพียงแค่จับคู่ส่วนนั้นกับฟังก์ชันที่ราบรื่นและเร่งความเร็วภาพเคลื่อนไหวตามระยะห่างจากศีรษะ

Image Morphing โดยทั่วไป

เกี่ยวกับอัลกอริทึม Beier–Neely morphing ฉันไม่พบการใช้งานแบบขนานหรือ GPU ที่เร่งความเร็ว ดังนั้นการคำนวณเฟรมระดับกลางบนภาพขนาดใหญ่ เช่น 2k*2k พิกเซลที่มีเส้นคุณลักษณะหลายสิบเส้นใช้เวลานานมาก ในอนาคตฉันวางแผนที่จะเขียนการใช้งานเหล่านี้ การใช้งานครั้งแรกจะใช้สำหรับการใช้งานในเครื่อง/เซิร์ฟเวอร์ผ่าน CUDA และครั้งที่สองจะใช้GPGPUและ shaders GPGPU นั้นน่าสนใจเป็นพิเศษเนื่องจากคุณสามารถสร้างสรรค์ได้มากขึ้น

มีหลายโครงการ (เช่นDiffMorph ) ที่มาพร้อมกับโครงข่ายประสาทเทียมในกระบวนการ morphing — เป็นการเร่งความเร็วของกระบวนการหรือเป็นกระบวนการที่สมบูรณ์ เหตุผลที่อยู่เบื้องหลังการใช้งานและการทดลองกับอัลกอริธึมเชิงกำหนดแบบเก่าที่ดีคือฉันต้องการดูเอกสารสองสามฉบับและดำเนินการเกี่ยวกับ GPU

โครงการด้านข้างดีมาก

เริ่มต้นจากการสร้างต้นแบบอย่างรวดเร็วเพียงเพื่อให้เห็นภาพเมฆพอยต์คลาวด์จากโครงการต่างๆ ของฉัน แต่ท้ายที่สุดฉันใช้เวลาประมาณหนึ่งสัปดาห์ในการทดสอบและเพิ่มคุณสมบัติใหม่เพียงเพื่อความสนุก และฉันยังได้เรียนรู้บางสิ่งระหว่างทางด้วย ด้านล่างนี้คุณจะพบลิงค์ไปยัง GitHub ของฉันพร้อมรหัสที่สมบูรณ์และไปยังเว็บไซต์สำหรับการสาธิตสด อย่าลังเลที่จะแยกหรือแม้แต่ทำการประชาสัมพันธ์ด้วยแนวคิดใหม่ ฉันวางแผนที่จะเพิ่มคุณสมบัติเพิ่มเติม แต่ฉันไม่ต้องการทำให้เสียไปมากกว่านี้ ข้อจำกัดความรับผิดชอบ ฉันไม่ใช่นักพัฒนาเว็บ ดังนั้นฉันอาจใช้โครงสร้างแปลกๆ แต่ฉันยินดีรับฟังคำวิจารณ์!

ที่เก็บ Github
การสาธิตเว็บสด

โครงการเป็นเว็บไซต์แบบสแตติกที่ไม่มีความเป็นไปได้ในการอัปโหลดรูปภาพที่กำหนดเอง สำหรับการใช้งานแบบกำหนดเอง คุณต้องโคลนพื้นที่เก็บข้อมูลและจัดเตรียมรูปภาพ / แผนที่เชิงลึกด้วยตัวคุณเอง หากคุณเคยใช้ conda และ npm คุณจะไม่มีปัญหาเกี่ยวกับ MiDaS และเครื่องมืออื่นๆ ฉันอาจทำให้โครงการเป็นไดนามิกและสร้างแผนที่ความลึก / ภาพ morphing / 3D ในฝั่งเซิร์ฟเวอร์ แต่ขึ้นอยู่กับเวลาที่เป็นไปได้ของฉัน แต่ฉันแน่ใจว่ากำลังวางแผนที่จะเพิ่มคุณสมบัติและการปรับโครงสร้างใหม่ บางสถานการณ์ทำให้หน้าโหลดซ้ำบน iPhone ขณะใช้ Safari หรือ Chrome ซึ่งจะดีบักในเร็วๆ นี้

ขอขอบคุณที่สละเวลาและอ่านโพสต์นี้ แล้วพบกันใหม่!