美文网首页
ThinkPHP3.2+Krpano实现全景图

ThinkPHP3.2+Krpano实现全景图

作者: 简跃 | 来源:发表于2019-06-26 17:17 被阅读0次

    为了实现全立体的3D全景图效果,我们采用了Krpano软件将普通鱼眼图片渲染为720°全景图。

    首先下载软件Krpano全景图生成软件,提取码z2zu,其中包含Linux版本及Win版本以及简单的使用手册文件。

    其实简单的使用只需两步,第一步是将上传的图片生成显示全景图需要的图片,第二步是根据全景图的显示规则和配置文件将全景图显示出来。

    上传图片并生成全景图

    原理很简单,将图片上传到服务器,然后将服务器的图片通过Krpano软件生成全景图,并将生成后的图片转移到统一的目录中。

    在开始上传图片前,需要修改Krpano的配置文件Krpano/templates/normal.config如下:

    # krpano 1.19
    
    # 引入基本设置
    include basicsettings.config
    # 全景图类型 自动 如果可以识别自动,不能识别图片会询问处理方法
    panotype=autodetect
    hfov=360
    
    # 输出设置
    flash=true
    html5=true
    
    # convert spherical/cylindrical to cubical
    converttocube=true
    converttocubelimit=360x45
    
    # multiresolution settings
    multires=true
    maxsize=8000
    maxcubesize=2048
    
    # 输出图片路径
    tilepath=%INPUTPATH%/pano/%BASENAME%.tbs-pano/3d-pano-[c].jpg
    
    # 输出预览图图片设置
    preview=true
    graypreview=false
    previewsmooth=25
    previewpath=%INPUTPATH%/pano/%BASENAME%.tbs-pano/3d-pano-preview.jpg
    
    # 输出缩略图图片设置
    makethumb=true
    thumbsize=240
    thumbpath=%INPUTPATH%/pano/%BASENAME%.tbs-pano/3d-pano-thumb.jpg
    

    上传接口代码如下:

    public function upload_3d_pic()
    {
        $file = $_FILES["imgUpload"];
        $u_name =$file['name'];
        $u_temp_name =$file['tmp_name'];
        $u_size =$file['size'];
        
        // 生成 一个随机字符串
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123tbs456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol)-1;
        for($i=0;$i<$length;$i++){
            $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }
        
        //$md5Code 会做为文件夹的名字 跟文件的名字,要保持唯一性
        $md5Code =md5_16bit(hash("sha256",$u_name.time().$rand_char)).$str;
        $datePath =date("Y-m-d",time());
    
        $root_path ='./upload_3dpic/';
        $url_path ='/upload_3dpic/';    //外部访问url
        $f_up_to_path =$root_path .'/'. $datePath.'/'.$md5Code;
        if(!file_exists($f_up_to_path)){
            mkdir($f_up_to_path, 0777, true);
        }
        $type = strtolower(substr($u_name, strrpos($u_name, '.') + 1));
        $img_file_name =$md5Code."." . $type;
    
        $saveFileName = $f_up_to_path."." . $type;
        $true_img_url =$url_path . $datePath.'/'.$md5Code."." . $type; //外部访问链接
        if (!move_uploaded_file($u_temp_name, $saveFileName)) {
            $this->ajaxReturn(array("error_code"=>250,"msg"=>"图片上传失败,请稍后重试!","return"=>"move pic fail>>temp_name=".$u_temp_name.">>save file name=".$saveFileName));
        } else {
            @rmdir($f_up_to_path);
        }
    
        //判断文件是否存在
        if(file_exists($saveFileName)){
            //如果存在 则生成 全景图
            $this->create_pano_pic($saveFileName);
            // 如果 此时没有生成图片 需要删除上传图片并报错 平面图可能生成不了图片
            $dirName = dirname($saveFileName) . '/pano' . '/' . $md5Code . '.tbs-pano';
            if ( !file_exists($dirName) ) {
                unlink($saveFileName); // 删除文件
                $this->ajaxReturn(array('error_code'=>250,"msg"=>"上传图片不能生成全景图"));
            }
    
            //移动全景图到指定的目录 图片在哪里全景图将会生成在那个目录
            $mvres = $this->mv_to_pano_path($saveFileName,$img_file_name);
            if ( $mvres === false ) {
                $this->ajaxReturn(array('error_code'=>250,"msg"=>"移动文件失败"));
            }
        }else{
    
            $this->ajaxReturn(array('error_code'=>250,"msg"=>"img not exists!",'img_url'=>$true_img_url));
        }
        // 移动后的缩略图路径
        $thumb_url = $url_path . 'TreeDPic/' . $md5Code . '/pano/' . $md5Code . '.tbs-pano/3d-pano-thumb.jpg';
        $this->ajaxReturn(array(
            'error_code'=>0,
            'msg'=>"sucess",
            'img_url'=>$true_img_url,
            "pano_name"=>$md5Code,
            'thumb_url'=>$thumb_url)
         );
    }
    
    /***
    * @param string $img_path
    * @return string
    * 将当前传入的图片 渲染成为全景图
    */
    private function create_pano_pic($img_path="")
    {
        if(empty($img_path)){
            return $img_path;
        }
        if(!file_exists($img_path)){
            return "图片不存在!";
        }
        //软件注册码
        $r_code ="FXsqTqaGNSZER5dSETEm+VzQEh9sWSa5DZMFsSmMxYV9GcXs8W3R8A/mWXrGNUceXvrihmh28hfRF1ivrW0HMzEychPvNiD8B/4/ZzDaUE9Rh6Ig22aKJGDbja1/kYIqmc/VKfItRE2RTSOIbIroxOtsz626NIpxWksAAifwhpNwuPXqDQpz2sRUMBzoPqZktpkItoSenN2mKd8Klfx7pOuB6CIK3e1CDXgyndqOt2mWybLZcU/wfJVAecfxk15ghiqrzaDsbqrdABDowg==";
    
        $pano_path=C("KRPANO_PATH"); //krpano 路径 自己配置
    
        $pano_tools ="krpanotools";
    
        //krpano 生成图片的命令
        $dealFlat = ''; // 处理 非球面图
        if(PHP_OS == 'WINNT'){
            $pano_path=$pano_path."Win";
            $pano_tools ="krpanotools32.exe";
        } else {
            // 上传平面图时 直接跳过图片生成 否则会一直等待
            $dealFlat = 'echo -e "0\n" | '; 
        }
        
        $kr_command = $dealFlat . $pano_path . "/".$pano_tools." makepano -config=" . $pano_path . "/templates/normal.config ";
    
        try{
            //在生成图片之前 先注册一下码,要不生成的全景图会有水印
            exec( $pano_path . '/'.$pano_tools.' register ' .$r_code);
            $kr_command =$kr_command.$img_path;
            //执行生成图片命令
            exec($kr_command, $log, $status);
        } catch (\Exception $e){
            $this->ajaxCallMsg(250,$e->getMessage());
        }
        return true;
    }
    
    /**
    * @param $pano_img_path
    * @return string
    * 全景图生成后再调用这个方法,把全景图移到对应的目录供 xml 文件获取内容
    */
    private function mv_to_pano_path($pano_img_path,$img_name){
        $ig_name =explode(".",$img_name)[0];
        $root_path = './upload_3dpic/';
    
        if(!file_exists($pano_img_path) ||empty($pano_img_path)){
            $this->up_error_log($pano_img_path.'》》图片路径文件不存在');
            return '';
        }
    
        $now_path =dirname($pano_img_path);//获取当前文件目录
    
        if ($dh = @opendir($now_path)){
            //打开目录
            while (($file = readdir($dh)) !== false){
                //循环获取目录的 文件
                if (($file != '.') && ($file != '..')) {
                    //如果文件不是.. 或 . 则就是真实的文件
                    if($file=="pano"){
                        //全景图切片目录
                        $t_d_path =$root_path .'TreeDPic/'. $ig_name;
    
                        if(!file_exists($t_d_path)){
                            //不存在就创建
                            @mkdir($t_d_path, 0777, true);
                        }
                        if(file_exists($t_d_path.'/'.$file)){
                            //判断是否已经存在 当前名字的  全景图 文件
                            return false;
                        }else{
                            //否则就 把 当前上传的生成 的全景文件切片,移动到指定的目录
                            rename($now_path.'/'.$file,$t_d_path.'/'.$file);
                        }
                    }else if ($file !==$img_name){
                        //删除不是 原图片的文件
                        if(is_dir($file)){
                            $this->deleteDir($now_path.'/'.$file);
                        }else{
                            @unlink($now_path.'/'.$file);
                        }
                    }else{
                        return false;
                    }
                }
            }
            closedir($dh);
        }else{
            return false;
        }
    
    }
    /**
    * @param $dir
    * @return bool
    * 删除文件夹及文件
    */
    private  function deleteDir($dir)
    {
        if (!$handle = @opendir($dir)) {
            return false;
        }
        while (false !== ($file = readdir($handle))) {
            if ($file !== "." && $file !== "..") {       //排除当前目录与父级目录
                $file = $dir . '/' . $file;
                if (is_dir($file)) {
                    $this->deleteDir($file);
                } else {
                    @unlink($file);
                }
            }
        }
        @rmdir($dir);
    }
    

    此时,我们已经可以通过上传接口上传图片并通过Krpano渲染图片为全景图了。

    显示全景图

    要将图片显示出来,我们必须按照Krpano规则生成必须的xml配置文件。

    我们将根据上传图片是生成的唯一码作为依据生成全景图。

    // 解析XML文件
    public function panorama_xml(){
        $code =I("code");
        $cutNum =intval(I("cutNum"));
        $url_path = '/upload_3dpic/';   
        // 切割模式分为 6图 和 12图
        if(!in_array($cutNum,array(6,12))){
            $this->error();
        }
        $this->echoSixXml($url_path,$code);
    }
    
    private function echoSixXml($url_path,$code=""){
        echo "<krpano  version=\"1.19\" title=\"Virtual Tour\">
                <!-- the skin -->
                <!-- <include url=\"/3dpic/pano/sixDefaultXml/\" />--> 
    
                <!-- 视图设置 <view hlookat=\"0\" vlookat=\"0\" maxpixelzoom=\"1.0\" fovmax=\"150\" limitview=\"auto\" /> -->
                
    
                <skin_settings maps=\"false\"
                       maps_type=\"google\"
                       maps_bing_api_key=\"\"
                       maps_google_api_key=\"\"
                       maps_zoombuttons=\"false\"
                       gyro=\"true\"
                       webvr=\"true\"
                       webvr_gyro_keeplookingdirection=\"false\"
                       webvr_prev_next_hotspots=\"true\"
                       littleplanetintro=\"false\"
                       title=\"true\"
                       thumbs=\"true\"
                       thumbs_width=\"120\" thumbs_height=\"80\" thumbs_padding=\"10\" thumbs_crop=\"0|40|240|160\"
                       thumbs_opened=\"false\"
                       thumbs_text=\"false\"
                       thumbs_dragging=\"true\"
                       thumbs_onhoverscrolling=\"false\"
                       thumbs_scrollbuttons=\"false\"
                       thumbs_scrollindicator=\"false\"
                       thumbs_loop=\"false\"
                       tooltips_buttons=\"false\"
                       tooltips_thumbs=\"false\"
                       tooltips_hotspots=\"false\"
                       tooltips_mapspots=\"false\"
                       deeplinking=\"false\"
                       loadscene_flags=\"MERGE\"
                       loadscene_blend=\"OPENBLEND(0.5, 0.0, 0.75, 0.05, linear)\"
                       loadscene_blend_prev=\"SLIDEBLEND(0.5, 180, 0.75, linear)\"
                       loadscene_blend_next=\"SLIDEBLEND(0.5,   0, 0.75, linear)\"
                       loadingtext=\"loading...\"
                       layout_width=\"100%\"
                       layout_maxwidth=\"814\"
                       controlbar_width=\"-24\"
                       controlbar_height=\"40\"
                       controlbar_offset=\"20\"
                       controlbar_offset_closed=\"-40\"
                       controlbar_overlap.no-fractionalscaling=\"10\"
                       controlbar_overlap.fractionalscaling=\"0\"
                       design_skin_images=\"vtourskin.png\"
                       design_bgcolor=\"0x2D3E50\"
                       design_bgalpha=\"0.8\"
                       design_bgborder=\"0\"
                       design_bgroundedge=\"1\"
                       design_bgshadow=\"0 4 10 0x000000 0.3\"
                       design_thumbborder_bgborder=\"3 0xFFFFFF 1.0\"
                       design_thumbborder_padding=\"2\"
                       design_thumbborder_bgroundedge=\"0\"
                       design_text_css=\"color:#FFFFFF; font-family:Arial;\"
                       design_text_shadow=\"1\"
                       />
                
        
                <scene name=\"{$code}\" title=\"{$code}\" onstart=\"\" thumburl=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-thumb.jpg\" lat=\"\" lng=\"\" heading=\"\">
            
                    <view hlookat=\"0.0\" vlookat=\"0.0\" fovtype=\"MFOV\" fov=\"120\" maxpixelzoom=\"2.0\" fovmin=\"70\" fovmax=\"140\" limitview=\"range\" vlookatmin=\"-58.156\" vlookatmax=\"58.156\" />
            
                    <preview url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-preview.jpg\" />
            
                    <image type=\"CUBE\" multires=\"true\" tilesize=\"512\">
                        <cube url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-%s.jpg\" />
                    </image>
                </scene>
                <!--<preview url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/preview.jpg\" />-->
    
                <image>
                    <cube url=\"{$url_path}TreeDPic/{$code}/pano/{$code}.tbs-pano/3d-pano-%s.jpg\" />
                </image>
    
            </krpano>";
        }
    

    其中scene并不会当前的效果图渲染出来,而是在我们在多张全景图之间进行选择的时候通过DOM.call("toggle_item_hotspots();");自动触发。

    设置显示页面的路由及方法:

    public function panorama(){
    
        //先 获取id (md5值)
        $code =trim(I("code"));
        //图片切割方式  6图(采集的是6图) 和12图(比较复杂建议生成图片 用6图 配置切割)
        $cutNum =intval(I("cutNum"));
        $this->assign("codeVal",$code);
        $this->assign("cutNum",$cutNum);
    
        $this->display();
    }
    

    相应的视图文件中:

    <!DOCTYPE html>
    <html>
    <head>
        <title>土拨鼠全景漫游图 - {$pageData.title}</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <meta http-equiv="x-ua-compatible" content="IE=edge" />
        <link rel="stylesheet" href="{$Think.TBS_STATIC}/common/css/new_base.css?v=1560493706" />
        <link rel="stylesheet" href="/res/impression/vtour/pc/krpano.css"/>
        <style>
            @-ms-viewport { width:device-width; }
            @media only screen and (min-device-width:800px) { html { overflow:hidden; } }
            html { height:100%; }
            body { height:100%; overflow:hidden; margin:0; padding:0; font-family:Arial, Helvetica, sans-serif; font-size:16px; color:#FFFFFF; background-color:#000000; }
            .loading{
                /* display: none; */
                width: 100%;
                height: 100%;
                position: absolute;
                top: 0;
                left: 0;
                z-index: 3;
                background-color: #fff;
                color:#333;
                z-index: 100;
            }
            .loadingimg {
                width: 184px;
                height: 108px;
                position: absolute;
                top: 50%;
                left: 50%;
                -webkit-transform: translateX(-50%) translateY(-50%);
                -moz-transform: translateX(-50%) translateY(-50%);
                -ms-transform: translateX(-50%) translateY(-50%);
                transform: translateX(-50%) translateY(-50%);
                text-align: center;
            }
            .loadingimg img {
                width: 100%;
                height: 100%;
            }
            .poiner {
                display: inline-block;
                width: 16px;
                vertical-align: bottom;
                overflow: hidden;
                /* animation: poiner 3s infinite step-start; */
            }
        </style>
    </head>
    <body>
    <script src="vtour/tour.js"></script>
        <div class="loading">
            <div class="loadingimg">
                <img src="{$Think.TBS_STATIC}/impression/vtour/img/loading.png">
                <div>加载中</div>
            </div>
        </div>
        <div id="pano" style="width:100%;height:100%;">
        </div>
    </body>
        <script>
            // var krpano = null;
            embedpano({
                swf: "{$Think.TBS_STATIC}/impression/vtour/tour.swf?v={$Think.CDNTIME}",
                xml: "/3dpic/panoxml/{$cutNum}_{$codeVal}",
                target: "pano",
                html5: "auto",
                mobilescale: 1.0,
                passQueryParameters: true,
            });
        </script>
        <script type="text/javascript" src="vtour/krpano.js"></script>
    </html>
    

    修改相应的静态资源文件的路径以适应自己的项目,此时访问我们的panorama方法就可以看到我们的全景图了。

    相关文章

      网友评论

          本文标题:ThinkPHP3.2+Krpano实现全景图

          本文链接:https://www.haomeiwen.com/subject/qvqhcctx.html