PHPackages                             ama-team/projection-framework - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. ama-team/projection-framework

ActiveLibrary[Framework](/categories/framework)

ama-team/projection-framework
=============================

0.2.1(8y ago)04542[6 issues](https://github.com/ama-team/php-projection-framework/issues)[2 PRs](https://github.com/ama-team/php-projection-framework/pulls)MITPHPPHP &gt;= 5.6

Since Oct 6Pushed 7y ago1 watchersCompare

[ Source](https://github.com/ama-team/php-projection-framework)[ Packagist](https://packagist.org/packages/ama-team/projection-framework)[ RSS](/packages/ama-team-projection-framework/feed)WikiDiscussions master Synced 2mo ago

READMEChangelogDependencies (14)Versions (9)Used By (0)

AmaTeam\\Image\\Projection
==========================

[](#amateamimageprojection)

[![Packagist](https://camo.githubusercontent.com/748f2e49bb7de1b2b199abaec22d93ea19b575feecf9d82ace9188ccedf3167e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616d612d7465616d2f70726f6a656374696f6e2d6672616d65776f726b2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ama-team/projection-framework)[![CircleCI branch](https://camo.githubusercontent.com/b4d3621ffd416a8608d8cadaee952b187c1939f2313f78b8bfe8103e771d7ff5/68747470733a2f2f696d672e736869656c64732e696f2f636972636c6563692f70726f6a6563742f6769746875622f616d612d7465616d2f7068702d70726f6a656374696f6e2d6672616d65776f726b2f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://circleci.com/gh/ama-team/php-projection-framework/tree/master)[![Coveralls](https://camo.githubusercontent.com/84cc8471857423b5a3460f88223b654c419f80d6b6b6b9c39199d73423e04b8d/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f616d612d7465616d2f7068702d70726f6a656374696f6e2d6672616d65776f726b2f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://coveralls.io/github/ama-team/php-projection-framework?branch=master)[![Scrutinizer](https://camo.githubusercontent.com/2054176af010025f65b7e4884cc02e456604c5dede4816a8dc421a7f122e3320/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f616d612d7465616d2f7068702d70726f6a656374696f6e2d6672616d65776f726b2f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/ama-team/php-projection-framework)[![Code Climate](https://camo.githubusercontent.com/b790a25e92c09e43ab3ba49b5407e878c06eb7ea66551ba2dc0926a3d770461b/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636c696d6174652f6769746875622f616d612d7465616d2f7068702d70726f6a656374696f6e2d6672616d65776f726b2e7376673f7374796c653d666c61742d737175617265)](https://codeclimate.com/github/ama-team/php-projection-framework)

Don't believe the coverage, it's [lying](https://github.com/sebastianbergmann/php-code-coverage/issues/409).

This is a simple library created for common work with sphere projections - at the moment of writing, to convert equirectangular and cube map projection types one into another.

Installation
------------

[](#installation)

```
composer require ama-team/image-projection-framework
```

PHP 5.6+ and GD / Imagick are required. All file operations are done through `league/flysystem`, which is explicitly set as dependency.

Usage
-----

[](#usage)

### Sixty-second start

[](#sixty-second-start)

```
use \AmaTeam\Image\Projection\Framework;
use \AmaTeam\Image\Projection\Framework\EncodingOptions;
use \AmaTeam\Image\Projection\Specification;
use \AmaTeam\Image\Projection\Image\Format;

$framework = new Framework();
$source = new Specification('equirect', 'tmp/uploads/source.jpg');
$target = new Specification(
    'cube',
    'static/pano/{f}/{x}/{y}.jpg',
    new Box(512, 512) // tile size,
    new Box(2, 2) // layout, amount of tiles horizontally and vertically
);

$options = (new EncodingOptions())->setQuality(0.9);

$framework->convert($source, $target, Format::JPEG, $options);
```

You'll need a `Framework` instance to start off, it will use `cwd()` as fs root. Next, `Framework` has `#convert()` and `#convertAll()` methods to turn one specification into other (others), optionally specifying format and encoding options (don't worry, it's already jpeg / 90% by default). Specification is basically a description of a pano: it's type, it's location, it's face size and tile size. Location allows several placeholders - {f} or {face}, {x} and {y} to automatically recognize / populate it with tile parameters. You won't need them at all for equirectangular images, while faces for cubemap are named using their first letters - u(p), f(ront), b(ack), l(eft), r(ight), d(own). Type is found using case-insensitive prefix search, so `EQUIRECTANGULAR` and `equirect` is basically the same.

Chances are you will be creating multi-resolution cube map out of equirectangular projection, and to lessen resource consumption, source projection should be read just once - this can be easily done using `#convertAll()`, which uses single reader:

```
$targets = array_map(function ($index) {
    $side = 256 * (int) pow($index, 2);
    $tileSide = min($side, 512);
    $size = $side / $side;
    $tileSize = new Box($tileSide, $tileSide);
    $layout = new Box($size, $size);
    $path = sprintf('static/pano/%d/{f}/{x}/{y}.jpg', $index);
    return new Specification('cube', $path, $tileSize, $layout);
}, range(0, 3));

$framework->convertAll($source, $targets);
```

This should do the trick.

The last thing to tell: **it is extremely slow**. The current processing model fills target projection pixel-by-pixel, so it takes a while (up to minute on my laptop, cheap servers may be quite slower). And yes, it will burn a single core as much as it can. Also don't forget that source projection has to fit in memory, don't blame me for your images being too big.

Conventions
-----------

[](#conventions)

Projections are represented as a three-level deep tile structures. Every projection consists of arbitrary amount of faces (six for cube map, one for equirectangular and planet), and each face is a two-dimension grid of tiles. Tile position, which is determined as {face, x, y}, is expressed through curly-braces placeholders, as in the main usage example. All projections follow that rule (even though equirectangular is unlikely to have multiple tiles), but that may change in future releases.

Framework assumes that all tiles have the exact sizes. Not following that rule may result in undefined behavior and errors.

Flysystem usage assumes that all paths are specified using slash (not backslash), and there are no absolute paths at all, all paths are relative to root.

Considerations
--------------

[](#considerations)

This library processes images by creating virtual sphere accessor from source projection and then using it to populate target projection, texel by texel. This raises following problems:

- To complete target projection, source projection has to be fully loaded into RAM. This could be quite a lot for big projections, and the library itself doesn't do any size validations, so you have to watch RAM yourself. Also, image loading adds time penalty itself.
- The main workload is giant loop that asks for color of specific sphere coordinates and then populates target image with that color. This results in {number of pixels} function calls, and that number is usually huge even for modern processor - generating 2048x2048 cube map will need to populate 24M pixels, and all that work would be done on single CPU core, and there is not much overhead to optimize. **It is slow as hell and not much can be done**. Generating such a projection for two minutes is OK, having one core occupied at 100% is expected as well. All you can do is to parallelize work on several cores by filtering which tiles should be generated; this, however, will require to load source projection into RAM for every core.

If you want to convert projections faster or exploit GPU, you will need to implement the same thing yourself, most probably using other language. Image processing was always a complex thing and PHP is not enough to solve it efficiently.

Filtering generated tiles, adding processors and listeners
----------------------------------------------------------

[](#filtering-generated-tiles-adding-processors-and-listeners)

All the specified above needs more fine work and other methods. `Framework` instance provides method `getConverter()` that allows more detailed work using methods `createConversion()` and `createConversions()`. Those will create an object ready to perform conversion, but not yet launched (which is done through `#run()` method), and which expose additional processing capabilities.

If you've considered parallelizing work by scheduling different tiles to different workers, you'll need to restrict some tiles from being created on particular worker. Filter functionality exists just for this case:

```
// only specified faces will be created
$filter = new FaceFilter('f', 'b');
$options = (new ConversionOptions())->setFilters([$filter]);
$framework
    ->getConverter()
    ->createConversion($source, $target, $options)
    ->run();
```

If you need to add some kind of watermark or apply custom antialiasing, you can do it via processor which is run on tile after it's generation:

```
$fxaaProcessor = new FXAAProcessor();
$watermarkProcessor = new WatermarkProcessor();
$conversion = $framework
    ->getConverter()
    ->createConversion($source, $target);
    // 50 is order in which processor will run, so it will run before
    // watermark processor
    ->addProcessor(50, $fxaaProcessor)
    ->addProcessor(99, $watermarkProcessor);
    ->run();
```

Last thing to mention is listeners - those are simply some side-effect generators that accept fully generated tiles. The most common example is SaveListener that is not included in conversion by default:

```
$filesystem = $framework->getRegistry()->getFilesystem();
$listener = new SaveListener($filesystem, Format::JPEG);
$conversion = $framework
    ->getConverter()
    ->createConversion($source, $target)
    ->addListener($listener)
    ->run();
```

This is what actually default methods in sixty second example do.

Adding custom projection type
-----------------------------

[](#adding-custom-projection-type)

All projections are quite the same: they are bunch of images with some rules of mapping sphere on it and vice versa. All you actually need is to implement `MappingInterface`, bury it in AbstractHandler child and register in framework:

```
$framework = new Framework();
$framework->register('LittlePlanet', new LittlePlanetHandler());
```

You're ready to go.

Contributing
------------

[](#contributing)

Feel free to fork and send PR to **dev** branch.

### Testing

[](#testing)

Testing is done using Codeception, and everything, except for directory structure and installable fixtures, is quite the usual.

To install the fixtures, simply run `test:setup` task:

```
bin/robo test:setup
```

This will download some public license test images from flickr and put them into `tests/Data/External/Projections`, as well as cur them into faces.

You can run tests by using `test` and `test:` commands:

```
bin/robo test
bin/robo test:acceptance
bin/robo test:unit --coverage
```

Acceptance tests are doing some real work mangling millions of texels, so you should never ever turn on coverage for them unless you are on Intel Stress Test team. They are already slow enough, believe me.

Following command will generate coverage and [Allure](https://github.com/allure-framework/allure) reports:

```
bin/robo test:report
```

### Dev branch state

[](#dev-branch-state)

[![CircleCI](https://camo.githubusercontent.com/f206349da62889a08177010becf6675ef6d773ecdaf93776a701a6634d33b376/68747470733a2f2f696d672e736869656c64732e696f2f636972636c6563692f70726f6a6563742f6769746875622f616d612d7465616d2f7068702d70726f6a656374696f6e2d6672616d65776f726b2f6465762e7376673f7374796c653d666c61742d737175617265)](https://circleci.com/gh/ama-team/php-projection-framework/tree/dev)[![Coveralls](https://camo.githubusercontent.com/18b9b9e3d4d858bc149e7c24eff6c63870febc723f5d64ddceed5c74a99de323/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f616d612d7465616d2f7068702d70726f6a656374696f6e2d6672616d65776f726b2f6465762e7376673f7374796c653d666c61742d737175617265)](https://coveralls.io/github/ama-team/php-projection-framework?branch=dev)[![Scrutinizer](https://camo.githubusercontent.com/5abc19cdc55d7b23d3eb5106daeebefb682b2404df91653c6e75380a305f823a/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f616d612d7465616d2f7068702d70726f6a656374696f6e2d6672616d65776f726b2f6465762e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/ama-team/php-projection-framework)

License
-------

[](#license)

MIT License

AMA Team, 2017

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity14

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~89 days

Total

3

Last Release

2962d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/14b9fb88f81469e01bb6e6299d7ad54a10e177039b284f9af8cacfe5f346c4d9?d=identicon)[etki](/maintainers/etki)

---

Top Contributors

[![etki](https://avatars.githubusercontent.com/u/1909762?v=4)](https://github.com/etki "etki (34 commits)")

---

Tags

panopanoramaprojectionprojection-mapping

###  Code Quality

TestsCodeception

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/ama-team-projection-framework/health.svg)

```
[![Health](https://phpackages.com/badges/ama-team-projection-framework/health.svg)](https://phpackages.com/packages/ama-team-projection-framework)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.6k509.9M17.0k](/packages/laravel-framework)[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[magento/community-edition

Magento 2 (Open Source)

12.1k52.1k10](/packages/magento-community-edition)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[laravel-zero/framework

The Laravel Zero Framework.

3371.4M369](/packages/laravel-zero-framework)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
