├── pc_error.exe ├── viewOctree.png ├── OctreeAlgorithm.pdf ├── README.md ├── entropyDecoding.m ├── showOccupancyCode.m ├── entropyCoding.m ├── DeOctree.m ├── LICENSE ├── run.m └── GenOctree.m /pc_error.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zb12138/PointCloud-Octree-Compression-/HEAD/pc_error.exe -------------------------------------------------------------------------------- /viewOctree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zb12138/PointCloud-Octree-Compression-/HEAD/viewOctree.png -------------------------------------------------------------------------------- /OctreeAlgorithm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zb12138/PointCloud-Octree-Compression-/HEAD/OctreeAlgorithm.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PointCloud-Octree-Compression 2 | 3 | Matlab code for PointCloud Octree Compression. 4 | 5 | This is a lossless point cloud compression MATLAB demo based on the octree occupancy (BFS) and arithmetic coding. 6 | 7 | Algorithm description is in the OctreeAlgorithm.pdf. 8 | 9 | ![](viewOctree.png) 10 | -------------------------------------------------------------------------------- /entropyDecoding.m: -------------------------------------------------------------------------------- 1 | function dtext = entropyDecoding(binPath) 2 | fileID = fopen(binPath); 3 | lengthtext = fread(fileID,1,'uint32'); 4 | lengthtable = fread(fileID,1,'uint16'); 5 | feqC = fread(fileID,lengthtable,'uint8'); 6 | offset =fread(fileID,1,'int32'); 7 | bin = fread(fileID,'ubit1'); 8 | fclose(fileID); 9 | % Entropy decoding 10 | feq = double(feqC(feqC~=0)); 11 | dtext = arithdeco(bin,feq,lengthtext); 12 | feqT = find(feqC); 13 | dtext = feqT(dtext)+offset; 14 | -------------------------------------------------------------------------------- /showOccupancyCode.m: -------------------------------------------------------------------------------- 1 | Lmax = size(Octree,2); 2 | pointNum = size(pt,1); 3 | pcolor = zeros(pointNum,Lmax); 4 | for level = 1:Lmax 5 | ptInOccupancy = arrayfun(@(S)S.childPoint,Octree(level).node,'UniformOutput',false)'; 6 | Occupancy = cell2mat(arrayfun(@(S)S.occupancyCode,Octree(level).node,'UniformOutput',false)'); 7 | Occupancy = bin2dec(num2str(Occupancy)); 8 | for i=1:length(ptInOccupancy) 9 | pIo = ptInOccupancy(i); 10 | pcolor(cell2mat(pIo{:}),level)=Occupancy(i); 11 | end 12 | end 13 | 14 | for i = 1:Lmax 15 | subplot(2,4,i); 16 | pcshow(pt,pcolor(:,i)); 17 | end 18 | -------------------------------------------------------------------------------- /entropyCoding.m: -------------------------------------------------------------------------------- 1 | % entropy coding 2 | function binsize = entropyCoding(text,binPath) 3 | % disp('entropyCoding...') 4 | offset = min(text)-1; 5 | text = text - offset; 6 | feq=tabulate(text); 7 | feqInt = zeros(1,max(text)); 8 | maxcount = max(feq(:,2)); 9 | for i=1:size(feq,1) 10 | feqInt(feq(i,1)) = max( uint8((feq(i,2)./ maxcount)*255) ,1); 11 | end 12 | counts = double(feqInt); 13 | counts(counts==0) = []; 14 | temp = nan(size(text)); 15 | for i=1:size(feq,1) 16 | temp(text==feq(i,1))=i; 17 | end 18 | text = temp; 19 | bin = arithenco(text,counts); 20 | lenthtext= uint32(length(text)); 21 | fileID = fopen(binPath,'w'); 22 | fwrite(fileID,lenthtext,'uint32'); 23 | fwrite(fileID,length(feqInt),'uint16'); 24 | fwrite(fileID,uint8(feqInt),'uint8'); 25 | fwrite(fileID,offset,'int32'); 26 | fwrite(fileID,bin,'ubit1'); 27 | fclose(fileID); 28 | binsize = dir(binPath); 29 | binsize = binsize.bytes; 30 | end 31 | -------------------------------------------------------------------------------- /DeOctree.m: -------------------------------------------------------------------------------- 1 | function ptRec = DeOctree(dtext) 2 | % DeOctree 3 | ptRec = DeOctree(dtext); 4 | function points = DeOctree(Codes) 5 | occupancyCode = flip(dec2bin(Codes)-'0',2); 6 | codeL = size(occupancyCode,1); 7 | N = ones(1,30); 8 | codcal = 1; 9 | L = 1; 10 | while codcal+N(L)-1<=codeL 11 | L = L+1; 12 | N(L+1)= sum(occupancyCode(codcal:codcal+N(L)-1,:),'all'); 13 | codcal = codcal+N(L); 14 | end 15 | Lmax = L; 16 | Octree(1:Lmax)=struct('node',[],'nodeNum',1); 17 | proot = struct('pos',[0,0,0]); 18 | Octree(1).node = proot; 19 | codei = 1; 20 | for L = 2:Lmax 21 | Octree(L).nodeNum = N(L+1); 22 | childId = 0; 23 | childNode(1:Octree(L).nodeNum) = struct('pos',[0,0,0]); 24 | for currentNode = Octree(L-1).node 25 | code =occupancyCode(codei,:); 26 | for bit = find(code) 27 | childId = childId+1; 28 | childNode(childId).pos = currentNode.pos+ bitshift((dec2bin(bit-1,3)-'0'),Lmax-L); 29 | end 30 | codei = codei+1; 31 | end 32 | Octree(L).node = childNode(1:Octree(L).nodeNum); 33 | end 34 | points = cell2mat({Octree(Lmax).node.pos}'); 35 | end 36 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 zb12138 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /run.m: -------------------------------------------------------------------------------- 1 | clear 2 | % Read file 3 | filename = 'chair2851.ply'; 4 | quanfilePath = strcat(filename,'enc.ply'); 5 | binPath = strcat(filename,'bin'); 6 | p = pcread(filename); 7 | pointNum = p.Count; 8 | points = p.Location; 9 | 10 | % Quantization 11 | qs = 1;% qs must be integer 12 | points = round((points - min(points))/qs); 13 | pt = unique(points,'rows'); 14 | 15 | % Save file after quantization 16 | if ~exist(quanfilePath,'file') 17 | pcwrite(pointCloud(pt),quanfilePath); 18 | end 19 | fprintf('input file: %s\n',filename); 20 | fprintf('quantized file Path: %s\n',quanfilePath); 21 | fprintf('encoding points: %d\n',pointNum); 22 | save('pathfile.mat','quanfilePath','filename','binPath'); 23 | 24 | % Generate octree 25 | [code,Octree] = GenOctree(pt); 26 | % Codes = dec2hex(code); 27 | % disp(["codes:",Codes]) 28 | % dlmwrite(strcat(filename,'Octree.txt'),Codes,'delimiter',''); 29 | fprintf('bpp before entropy coding:%f bit\n',length(code)*8/pointNum); 30 | % Entropy Coding 31 | text = code; 32 | binsize = entropyCoding(text,binPath); 33 | fprintf('bpp after entropy coding:%f bit\n',binsize*8/pointNum); 34 | fprintf('bin file: %s\n',binPath); 35 | 36 | %% Decoding 37 | % clear 38 | disp('decoding...') 39 | load('pathfile.mat') 40 | disp(['binPath: ',binPath]) 41 | % Entropy decoding 42 | dtext = entropyDecoding(binPath); 43 | 44 | % Decode Octree 45 | ptRec = qs*DeOctree(dtext); 46 | 47 | % Evaluate 48 | disp('evaluate...') 49 | decodPath = strcat(filename,'dec.ply'); 50 | pcwrite(pointCloud(single(ptRec)),decodPath); 51 | Cmd=['pc_error.exe' ,' -a ',quanfilePath ,' -b ',decodPath, ' -r ','1023']; %psnr = 10log10(3*p^2/max(mse)) e.g. p = 1023 52 | system(Cmd); 53 | 54 | %Show occupancy code 55 | showOccupancyCode 56 | -------------------------------------------------------------------------------- /GenOctree.m: -------------------------------------------------------------------------------- 1 | function [Codes,Octree] = GenOctree(points) 2 | tic 3 | mcode = Morton(points); 4 | Lmax = ceil((size(mcode,2)/3)); 5 | pointNum = size(mcode,1); 6 | pointID = 1:pointNum; 7 | nodeid = 0; 8 | proot.nodeid = nodeid; 9 | proot.childPoint={pointID}; 10 | proot.occupancyCode=[]; 11 | proot.parent=0; 12 | Octree(1:Lmax+1) =struct('node',[],'level',0); 13 | Octree(1).node=proot; 14 | % Octree(1).nodeNum = 1; 15 | for L=1:Lmax 16 | Octree(L+1).level = L; 17 | NodeTemp(1:min([pointNum,8^(L-1)])) = struct('nodeid',nan,'childPoint',{[]},'parent',0,'occupancyCode',[]); 18 | nodeNum = 0; 19 | for node = Octree(L).node 20 | for ptid = node.childPoint 21 | PId = ptid{:}; 22 | if isempty(PId) 23 | continue 24 | end 25 | PId = pointID(PId); 26 | nodeid=nodeid+1; 27 | Node.nodeid = nodeid; 28 | Node.childPoint=cell(1,8); 29 | Node.parent=node.nodeid; 30 | n = L-1; 31 | mn = mcode(PId,1+n*3:3+n*3); 32 | idn = bin2dec(mn)+1; 33 | for i = 1:8 34 | Node.childPoint(i)= {PId(idn==i)}; 35 | end 36 | % Node.occupancyCode = flip(~cellfun("isempty",Node.childPoint)); 37 | % fast code 38 | Node.occupancyCode = ismember(8:-1:1,idn); 39 | nodeNum = nodeNum+1; 40 | NodeTemp(nodeNum)=Node; 41 | end 42 | end 43 | Octree(L+1).node= NodeTemp(1:nodeNum); 44 | end 45 | Octree(1)=[]; 46 | toc 47 | % fprintf('bpp before entropy coding:%f bit\n',nodeid*8/pointNum); 48 | % Nodes = arrayfun(@(S)S.node,Octree,'UniformOutput',false); 49 | % Codes=cellfun(@(S)arrayfun(@(S)S.occupancyCode,S,'UniformOutput',false),Nodes,'UniformOutput',false); 50 | % Codes = bin2dec(num2str(cell2mat([Codes{:}]'))); 51 | Nodes = [Octree.node]'; 52 | Codes = bin2dec(num2str(cell2mat({Nodes.occupancyCode}'))); 53 | end 54 | 55 | function mcode= Morton(A) 56 | n = ceil(log2(max(A(:))+1)); 57 | x = dec2bin(A(:,1),n); 58 | y = dec2bin(A(:,2),n); 59 | z = dec2bin(A(:,3),n); 60 | m = cat(3,x,y,z); 61 | m = permute(m,[1,3,2]); 62 | mcode = reshape(m,size(x,1),[]); 63 | % mcode = bin2dec(mcode); 64 | end 65 | --------------------------------------------------------------------------------