PHP基于URL下载合成krpano切图平台的原图

步骤一作品解析获取id
<?php
class PanoPageTool
{
    public static function downloadWithNaming($url, $options = []) 
    {
        $defaults = [
            'save_dir' => './downloads/',
            'file_prefix' => '',
            'file_suffix' => '.html',
            'overwrite' => false
        ];
        $options = array_merge($defaults, $options);
        // 提取文件名
        $path = parse_url($url, PHP_URL_PATH);
        $baseName = basename($path);
        if (empty($baseName) || $baseName === 't') {
            $fileName = $options['file_prefix'] . uniqid() . $options['file_suffix'];
        } else {
            $fileName = $options['file_prefix'] . $baseName . $options['file_suffix'];
        }
        $filePath = rtrim($options['save_dir'], '/') . '/' . $fileName;
        // 检查文件是否已存在
        if (!$options['overwrite'] && file_exists($filePath)) {
            return [
                'success' => false,
                'error' => '文件已存在',
                'file_path' => $filePath
            ];
        }
        // 下载内容
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_USERAGENT => 'Mozilla/5.0',
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_TIMEOUT => 30
        ]);
        $content = curl_exec($ch);
        $info = curl_getinfo($ch);
        curl_close($ch);
        if ($content && $info['http_code'] === 200) {
            // 确保目录存在
            if (!is_dir($options['save_dir'])) {
                mkdir($options['save_dir'], 0755, true);
            }
            if (file_put_contents($filePath, $content)) {
                return [
                    'success' => true,
                    'file_path' => $filePath,
                    'file_name' => $fileName,
                    'size' => filesize($filePath),
                    'url' => $url
                ];
            }
        }
        return [
            'success' => false,
            'error' => '下载失败',
            'http_code' => $info['http_code'] ?? 0,
            'url' => $url
        ];
    }
    public static function extractPanoDataIdRegex($htmlContent)
    {
        // 正则表达式匹配id="pano"的div和data-id属性
        $pattern = '/<div\s+id="pano"[^>]*data-id="([^"]*)"[^>]*>/i';
        if (preg_match($pattern, $htmlContent, $matches)) {
            return [
                'success' => true,
                'data_id' => $matches[1],
                'method' => 'regex'
            ];
        }
        // 备用模式:可能属性顺序不同
        $pattern2 = '/<div[^>]*data-id="([^"]*)"[^>]*id="pano"[^>]*>/i';
        if (preg_match($pattern2, $htmlContent, $matches)) {
            if (!empty($matches[1])) {
                return [
                    'success' => true,
                    'data_id' => $matches[1],
                    'method' => 'regex_alt'
                ];
            }
        }
        return [
            'success' => false,
            'error' => '未找到匹配的div元素或data-id属性'
        ];
    }
}
步骤二全景图还原

多层切图仅需还原最高分辨率

<?php
class KrpanoRestorer {
    private $baseUrl;
    
    public function __construct($baseUrl = '') {
        $this->baseUrl = rtrim($baseUrl, '/');
    }
    
    /**
     * 完整还原流程:下载 → 拼接 → 转换
     */
    public function fullRestoreProcess($xmlContent, $outputDir = 'output/') {
        $scenes = $this->parseScenes($xmlContent);
        
        foreach ($scenes as $scene) {
            echo "开始处理场景: {$scene['name']}\n";
            
            // 1. 下载所有瓦片
            $tilesDir = $this->downloadAllTiles($scene, $outputDir);
            
            // 2. 使用 KRPano 工具生成全景图
            $outputFile = $outputDir . $scene['name'] . '_equirectangular.jpg';
            $this->generateWithKrpanoTools($tilesDir, $scene, $outputFile);
            
            echo "场景 {$scene['name']} 处理完成: {$outputFile}\n";
        }
    }
    
    /**
     * 下载场景的所有瓦片
     */
    private function downloadAllTiles($scene, $baseOutputDir) {
        $level = $scene['levels'][0]; // 使用最高分辨率层级
        $tilesize = (int)$scene['tilesize'];
        $width = (int)$level['tiledimagewidth'];
        $height = (int)$level['tiledimageheight'];
        
        // 计算瓦片数量
        $cols = ceil($width / $tilesize);
        $rows = ceil($height / $tilesize);
        
        $faces = ['l', 'f', 'r', 'b', 'u', 'd'];
        $tilesDir = $baseOutputDir . 'tiles/' . $scene['name'] . '/';
        
        foreach ($faces as $face) {
            $faceDir = $tilesDir . $face . '/';
            if (!file_exists($faceDir)) {
                mkdir($faceDir, 0777, true);
            }
            
            echo "下载 {$face} 面瓦片...\n";
            
            for ($row = 1; $row <= $rows; $row++) {
                for ($col = 1; $col <= $cols; $col++) {
                    $tileUrl = $this->buildTileUrl($level['cube_url'], $face, $row, $col);
                    $tileFile = $faceDir . "tile_{$row}_{$col}.jpg";
                    
                    if (!file_exists($tileFile)) {
                        $this->downloadTile($tileUrl, $tileFile);
                    }
                }
            }
        }
        
        return $tilesDir;
    }
    
    /**
     * 构建瓦片URL
     */
    private function buildTileUrl($urlPattern, $face, $row, $col) {
        return $this->baseUrl . str_replace(
            ['%s', '%v', '%h'],
            [$face, $row, $col],
            $urlPattern
        );
    }
    
    /**
     * 下载单个瓦片
     */
    private function downloadTile($url, $outputPath) {
        $context = stream_context_create([
            'http' => [
                'timeout' => 30,
                'header' => "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\n"
            ]
        ]);
        
        $content = @file_get_contents($url, false, $context);
        if ($content && file_put_contents($outputPath, $content)) {
            echo ".";
            return true;
        }
        
        echo "F"; // 下载失败
        return false;
    }
    
    /**
     * 使用 KRPano 工具生成全景图
     */
    private function generateWithKrpanoTools($tilesDir, $scene, $outputFile) {
        $level = $scene['levels'][0];
        $tilesize = (int)$scene['tilesize'];
        
        // 首先拼接每个面的完整图像
        $stitchedDir = $tilesDir . 'stitched/';
        if (!file_exists($stitchedDir)) {
            mkdir($stitchedDir, 0777, true);
        }
        
        $faces = [
            'l' => 'left',
            'f' => 'front',
            'r' => 'right', 
            'b' => 'back',
            'u' => 'up',
            'd' => 'down'
        ];
        
        $stitchedFiles = [];
        foreach ($faces as $faceCode => $faceName) {
            $stitchedFile = $stitchedDir . $faceName . '.jpg';
            $this->stitchFace($tilesDir . $faceCode . '/', $stitchedFile);
            $stitchedFiles[$faceCode] = $stitchedFile;
        }
        
        // 构建 KRPano 命令
        $krpanoToolsPath = '/www/wwwroot/test/krpano/krpanotools'; //KRPano 工具路径
        
        // 方法1:使用明确的文件参数
        $command = sprintf(
            '%s cube2sphere -l="%s" -f="%s" -r="%s" -b="%s" -u="%s" -d="%s" -o="%s" -jpegquality=90',
            $krpanoToolsPath,
            $stitchedFiles['l'],
            $stitchedFiles['f'],
            $stitchedFiles['r'],
            $stitchedFiles['b'],
            $stitchedFiles['u'],
            $stitchedFiles['d'],
            $outputFile
        );
        
        echo "执行命令: " . $command . "\n";
        
        exec($command, $output, $returnCode);
        
        if ($returnCode === 0) {
            echo "KRPano 转换成功: {$outputFile}\n";
            return true;
        } else {
            echo "KRPano 转换失败 (" . $returnCode . "): " . implode("\n", $output) . "\n";
            return false;
        }
    }
    
    /**
     * 拼接某个面的所有瓦片成完整图像
     */
    private function stitchFace($faceDir, $outputFile) {
        // 获取所有瓦片文件并按行列排序
        $tiles = [];
        $files = glob($faceDir . 'tile_*.jpg');
        
        foreach ($files as $file) {
            if (preg_match('/tile_(\d+)_(\d+)\.jpg$/', basename($file), $matches)) {
                $row = (int)$matches[1];
                $col = (int)$matches[2];
                $tiles[$row][$col] = $file;
            }
        }
        
        if (empty($tiles)) {
            return false;
        }
        
        // 获取网格尺寸和瓦片大小
        $rows = count($tiles);
        $cols = count($tiles[1]); // 假设第一行有所有列
        
        $firstTile = imagecreatefromjpeg($tiles[1][1]);
        $tileWidth = imagesx($firstTile);
        $tileHeight = imagesy($firstTile);
        imagedestroy($firstTile);
        
        // 创建完整的面图像
        $faceWidth = $cols * $tileWidth;
        $faceHeight = $rows * $tileHeight;
        $faceImage = imagecreatetruecolor($faceWidth, $faceHeight);
        
        // 拼接所有瓦片
        for ($row = 1; $row <= $rows; $row++) {
            for ($col = 1; $col <= $cols; $col++) {
                if (isset($tiles[$row][$col])) {
                    $tileImg = imagecreatefromjpeg($tiles[$row][$col]);
                    $x = ($col - 1) * $tileWidth;
                    $y = ($row - 1) * $tileHeight;
                    imagecopy($faceImage, $tileImg, $x, $y, 0, 0, $tileWidth, $tileHeight);
                    imagedestroy($tileImg);
                }
            }
        }
        
        // 保存拼接后的面图像
        imagejpeg($faceImage, $outputFile, 90);
        imagedestroy($faceImage);
        
        return true;
    }
    
    /**
     * 解析场景信息
     */
    private function parseScenes($xmlContent) {
        $xml = simplexml_load_string($xmlContent);
        $scenes = [];
        
        foreach ($xml->scene as $scene) {
            $sceneInfo = [
                'name' => (string)$scene['name'],
                'view_id' => (string)$scene['view_id'],
                'tilesize' => (string)$scene->image['tilesize'],
                'levels' => []
            ];
            
            foreach ($scene->image->level as $level) {
                $sceneInfo['levels'][] = [
                    'tiledimagewidth' => (string)$level['tiledimagewidth'],
                    'tiledimageheight' => (string)$level['tiledimageheight'],
                    'cube_url' => (string)$level->cube['url']
                ];
            }
            
            $scenes[] = $sceneInfo;
        }
        
        return $scenes;
    }
}
?>
调用还原类还原图片
<?php
include_once('PanoPageTool.php');
include_once('KrpanoRestorer.php');

class DownloadPanoProjectPics
{
    private $url;
    private $project_html_file_name;
    private $project_id = 0;
    public function __construct($url)
    {
        $this->url = $url;
    }
    //步骤1:下载作品HTML页面
    public function step1()
    {
        $options = [
            'save_dir' => './my_pages/',
            'file_prefix' => 'page_',
            'overwrite' => true
        ];
        $result = PanoPageTool::downloadWithNaming($this->url ,$options);
        if($result['success'])
        {
            $this->project_html_file_name = $result['file_name'];
            echo "步骤1:成功下载作品页面: " . $result['file_name'] . "\n";
        }else{
            echo "步骤1:失败\n";
            exit;
        }
    }
    //步骤2: 作品页面正则匹配作品id
    public function step2()
    {
        $content = file_get_contents("./my_pages/".$this->project_html_file_name);
        $result = PanoPageTool::extractPanoDataIdRegex($content);
        if($result['success']){
            $this->project_id = $result['data_id'];
            echo "步骤2:解析project_id成功,准备下载xml文件\n";
        }else{
            echo "步骤2:解析project_id失败\n";
            exit;
        }
    }
    //步骤3:下载xml文件
    public function step3()
    {
        $url = "https://xxx/xml/".$this->project_id.".html";
        file_put_contents("./my_pages/".$this->project_id.".html",file_get_contents($url));
        if(file_exists(__DIR__."/my_pages/".$this->project_id.".html"))
        {
            echo "步骤3:下载xml文件成功\n";
        }else{
            echo "步骤3:下载xml文件失败\n";
        }
    }
    //步骤4:解析xml文件 解析图片 f b l r u d
    public function step4()
    {
        $restorer = new KrpanoRestorer('https://xxx/');
        $xmlContent = file_get_contents("./my_pages/".$this->project_id.".html");
        // 设置更大的内存限制和时间限制
        ini_set('memory_limit', '1024M');
        set_time_limit(0);
        $restorer->fullRestoreProcess($xmlContent, 'restored_output/'.$this->project_id.'/');
    }

    public function start()
    {
        $this->step1();
        $this->step2();
        $this->step3();
        $this->step4();
    }
}
$tool = new DownloadPanoProjectPics("https://xxx/pano_id");
$tool->start();
?>

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注