diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 49dfd1fe..01fa50e2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -7,26 +7,30 @@ on: jobs: tests: - strategy: - matrix: - node: - - 20.x - - # Packages 'firefox' and 'chromium' are pre-installed. - # - # https://github.com/actions/virtual-environments/blob/ubuntu20/20210302.0/images/linux/Ubuntu2004-README.md - runs-on: ubuntu-20.04 - name: Node.js ${{ matrix.node }} + runs-on: ubuntu-24.04 # Upgrade to Ubuntu 24.04 + name: Node.js CI steps: - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node }} + - name: Use Node.js from .nvmrc uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} + node-version-file: ".nvmrc" + + - name: Install ChromiumHeadless + run: | + sudo apt-get update + sudo apt-get install -y chromium-browser + + - name: Get npm cache directory + id: npm-cache-dir + shell: bash + run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT} + - uses: actions/cache@v4 + id: npm-cache with: - path: ~/.npm + path: ${{ steps.npm-cache-dir.outputs.dir }} key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml new file mode 100644 index 00000000..bac5cda7 --- /dev/null +++ b/.github/workflows/build-python.yml @@ -0,0 +1,120 @@ +name: Build and Test Python Bindings + +on: + push: + pull_request: + +jobs: + build-linux: + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.8' + + - name: Cache pip packages + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libjpeg9 + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install numpy setuptools wheel pybind11 + + - name: Update submodules + run: git submodule update --init + + - name: Build the Python bindings + run: | + cd python-bindings + python setup.py build_ext --inplace + + - name: Test the build + run: | + cd python-bindings + python test.py + + - name: Run unit tests + run: | + cd python-bindings + python -m unittest discover -s . -p 'unittests.py' + + build-windows: + runs-on: windows-2022 + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.8' + + - name: Cache pip packages + uses: actions/cache@v4 + with: + path: ~\AppData\Local\pip\Cache + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Cache vcpkg + uses: actions/cache@v4 + with: + path: | + vcpkg + !vcpkg/.git + key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }} + + - name: Install vcpkg + run: | + git clone https://github.com/microsoft/vcpkg.git + .\vcpkg\bootstrap-vcpkg.bat + + - name: Install libjpeg-turbo + run: .\vcpkg\vcpkg install libjpeg-turbo + + - name: Install pthreads + run: .\vcpkg\vcpkg install pthreads + + - name: Add vcpkg to path + shell: bash + run: echo "vcpkg/installed/x64-windows/bin" >> $GITHUB_PATH + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install numpy setuptools wheel pybind11 + + - name: Update submodules + run: git submodule update --init + + - name: Build the Python bindings + run: | + cd python-bindings + python setup.py build_ext --inplace + + - name: Test the build + run: | + cd python-bindings + python test.py + + - name: Run unit tests + run: | + cd python-bindings + python -m unittest discover -s . -p 'unittests.py' \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7dc647d4..0c66dc4b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,32 +8,34 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 # Upgrade to Ubuntu 24.04 - strategy: - matrix: - node-version: [20.x] steps: - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} + + - name: Use Node.js from .nvmrc uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: ".nvmrc" + - uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- + - name: Set output id: vars run: echo name=tag::${GITHUB_REF#refs/*/} >> $GITHUB_OUTPUT + - name: Check output env: RELEASE_VERSION: ${{ steps.vars.outputs.tag }} run: | echo $RELEASE_VERSION echo ${{ steps.vars.outputs.tag }} + - uses: actions/upload-artifact@v4 with: name: build @@ -42,15 +44,22 @@ jobs: !build/libar.o !build/libar_td.o !build/libar_simd.o + - run: git submodule update --init + - run: npm install + - run: docker run -dit --name emscripten -v $(pwd):/src emscripten/emsdk:3.1.69 bash + - run: docker exec emscripten npm run build + - uses: actions/upload-artifact@v4 with: name: dist path: dist + - run: npm run build-ts + - name: Commit changes if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} uses: EndBug/add-and-commit@v9 @@ -58,6 +67,7 @@ jobs: default_author: github_actions message: new build files from action add: '["build", "dist"]' + - name: Release uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') diff --git a/.gitignore b/.gitignore index d747e16b..7dcb4942 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ build/*.a node_modules docs/* emscripten/build +python-bindings/build +python-bindings/*.so +python-bindings/deps/libjpeg/* +python-bindings/__pycache__ diff --git a/package.json b/package.json index afc29ef7..37fc0675 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "build-docker": "docker exec emscripten-jsartoolkitnft npm run build", "build-docker-no-libar": "docker exec emscripten-jsartoolkitnft npm run build-no-libar", "test-browser": "karma start --browsers Chrome,Firefox,ChromeHeadless,FirefoxHeadless", + "build-python-bindings": "cd python-bindings && python setup.py build_ext --inplace", "test": "karma start", "watch": "./node_modules/.bin/watch 'npm run build' ./js/", "format-check": "prettier --check .", diff --git a/python-bindings/ARToolKitNFT_py.cpp b/python-bindings/ARToolKitNFT_py.cpp new file mode 100644 index 00000000..7ab23217 --- /dev/null +++ b/python-bindings/ARToolKitNFT_py.cpp @@ -0,0 +1,561 @@ +#include "ARToolKitNFT_py.h" +#include + +ARToolKitNFT::ARToolKitNFT() + : id(0), paramLT(NULL), videoFrame(NULL), videoFrameSize(0), + videoLuma(NULL), width(0), height(0), + detectedPage(-2), // -2 Tracking not inited, -1 tracking inited OK, >= 0 + // tracking online on page. + surfaceSetCount(0), // Running NFT marker id + arhandle(NULL), ar3DHandle(NULL), kpmHandle(NULL), ar2Handle(NULL), +#if WITH_FILTERING + ftmi(NULL), filterCutoffFrequency(60.0), filterSampleRate(120.0), +#endif + nearPlane(0.0001), farPlane(1000.0), + patt_id(0) // Running pattern marker id +{ + ARLOGi("init ARToolKitNFT constructor...\n"); +} + +ARToolKitNFT::~ARToolKitNFT() { + teardown(); +} + +void matrixLerp(ARdouble src[3][4], ARdouble dst[3][4], + float interpolationFactor) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 4; j++) { + dst[i][j] = dst[i][j] + (src[i][j] - dst[i][j]) / interpolationFactor; + } + } +} + +int ARToolKitNFT::passVideoData(py::array_t videoFrame, py::array_t videoLuma) { + auto videoFramePtr = static_cast(videoFrame.request().ptr); + auto videoLumaPtr = static_cast(videoLuma.request().ptr); + + this->videoFrame = videoFramePtr; + this->videoLuma = videoLumaPtr; + + return 0; +} + +py::dict ARToolKitNFT::getNFTMarkerInfo(int markerIndex) { + py::dict NFTMarkerInfo; + py::list pose; + + if (this->surfaceSetCount <= markerIndex) { + return py::dict("error"_a=MARKER_INDEX_OUT_OF_BOUNDS); + } + + float trans[3][4]; + +#if WITH_FILTERING + ARdouble transF[3][4]; + ARdouble transFLerp[3][4]; + memset(transFLerp, 0, 3 * 4 * sizeof(ARdouble)); +#endif + + float err = -1; + if (this->detectedPage == markerIndex) { + int trackResult = + ar2TrackingMod(this->ar2Handle, this->surfaceSet[this->detectedPage], + this->videoFrame, trans, &err); + +#if WITH_FILTERING + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 4; k++) { + transF[j][k] = trans[j][k]; + } + } + + bool reset = (trackResult < 0); + + if (arFilterTransMat(this->ftmi, transF, reset) < 0) { + ARLOGe("arFilterTransMat error with marker %d.", markerIndex); + } + + matrixLerp(transF, transFLerp, 0.95); +#endif + + if (trackResult < 0) { + ARLOGi("Tracking lost. %d", trackResult); + this->detectedPage = -2; + } else { + ARLOGi("Tracked page %d (max %d).\n", + this->surfaceSet[this->detectedPage], this->surfaceSetCount - 1); + } + } + + if (this->detectedPage == markerIndex) { + NFTMarkerInfo["id"] = markerIndex; + NFTMarkerInfo["error"] = err; + NFTMarkerInfo["found"] = 1; +#if WITH_FILTERING + for (auto x = 0; x < 3; x++) { + for (auto y = 0; y < 4; y++) { + pose.append(transFLerp[x][y]); + } + } +#else + for (auto x = 0; x < 3; x++) { + for (auto y = 0; y < 4; y++) { + pose.append(trans[x][y]); + } + } +#endif + NFTMarkerInfo["pose"] = pose; + } else { + NFTMarkerInfo["id"] = markerIndex; + NFTMarkerInfo["error"] = -1; + NFTMarkerInfo["found"] = 0; + for (auto x = 0; x < 3; x++) { + for (auto y = 0; y < 4; y++) { + pose.append(0.0f); + } + } + NFTMarkerInfo["pose"] = pose; + } + + return NFTMarkerInfo; +} + +int ARToolKitNFT::detectNFTMarker() { + + KpmResult *kpmResult = NULL; + int kpmResultNum = -1; + + if (this->detectedPage == -2) { + kpmMatching(this->kpmHandle, this->videoLuma); + kpmGetResult(this->kpmHandle, &kpmResult, &kpmResultNum); + +#if WITH_FILTERING + this->ftmi = arFilterTransMatInit(this->filterSampleRate, + this->filterCutoffFrequency); +#endif + + for (int i = 0; i < kpmResultNum; i++) { + if (kpmResult[i].camPoseF == 0) { + + float trans[3][4]; + this->detectedPage = kpmResult[i].pageNo; + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 4; k++) { + trans[j][k] = kpmResult[i].camPose[j][k]; + } + } + ar2SetInitTrans(this->surfaceSet[this->detectedPage], trans); + } + } + } + return kpmResultNum; +} + +KpmHandle *ARToolKitNFT::createKpmHandle(ARParamLT *cparamLT) { + KpmHandle *kpmHandle; + kpmHandle = kpmCreateHandle(cparamLT); + return kpmHandle; +} + +/*int ARToolKitNFT::getKpmImageWidth(KpmHandle *kpmHandle) { + return kpmHandleGetXSize(kpmHandle); +} + +int ARToolKitNFT::getKpmImageHeight(KpmHandle *kpmHandle) { + return kpmHandleGetYSize(kpmHandle); +}*/ + +int ARToolKitNFT::setupAR2() { + if ((this->ar2Handle = ar2CreateHandleMod(this->paramLT, this->pixFormat)) == + NULL) { + ARLOGe("Error: ar2CreateHandle.\n"); + kpmDeleteHandle(&this->kpmHandle); + } + // Settings for devices with single-core CPUs. + ar2SetTrackingThresh(this->ar2Handle, 5.0); + ar2SetSimThresh(this->ar2Handle, 0.50); + ar2SetSearchFeatureNum(this->ar2Handle, 16); + ar2SetSearchSize(this->ar2Handle, 6); + ar2SetTemplateSize1(this->ar2Handle, 6); + ar2SetTemplateSize2(this->ar2Handle, 6); + + this->kpmHandle = createKpmHandle(this->paramLT); + + return 0; +} + +nftMarker ARToolKitNFT::getNFTData(int index) { + // get marker(s) nft data. + return this->nftMarkers.at(index); +} + +/*********** + * Teardown * + ***********/ + +void ARToolKitNFT::deleteHandle() { + if (this->arhandle != NULL) { + arPattDetach(this->arhandle); + arDeleteHandle(this->arhandle); + this->arhandle = NULL; + } + if (this->ar3DHandle != NULL) { + ar3DDeleteHandle(&(this->ar3DHandle)); + this->ar3DHandle = NULL; + } + if (this->paramLT != NULL) { + arParamLTFree(&(this->paramLT)); + this->paramLT = NULL; + } +} + +int ARToolKitNFT::teardown() { + // TODO: Fix Cleanup luma. + // if(arc->videoLuma) { + // free(arc->videoLuma); + // arc->videoLuma = NULL; + // } + + if (this->videoFrame) { + free(this->videoFrame); + this->videoFrame = NULL; + this->videoFrameSize = 0; + } + + deleteHandle(); + + return 0; +} + +int ARToolKitNFT::setCamera(int id, int cameraID) { + + if (cameraParams.find(cameraID) == cameraParams.end()) { + return -1; + } + + this->param = cameraParams[cameraID]; + + if (this->param.xsize != this->width || this->param.ysize != this->height) { + ARLOGw("*** Camera Parameter resized from %d, %d. ***\n", this->param.xsize, + this->param.ysize); + arParamChangeSize(&(this->param), this->width, this->height, + &(this->param)); + } + + // ARLOGi("*** Camera Parameter ***\n"); + // arParamDisp(&(this->param)); + + deleteHandle(); + + if ((this->paramLT = arParamLTCreate(&(this->param), + AR_PARAM_LT_DEFAULT_OFFSET)) == NULL) { + ARLOGe("setCamera(): Error: arParamLTCreate.\n"); + return -1; + } + + // ARLOGi("setCamera(): arParamLTCreated\n..%d, %d\n", + // (this->paramLT->param).xsize, (this->paramLT->param).ysize); + + // setup camera + if ((this->arhandle = arCreateHandle(this->paramLT)) == NULL) { + ARLOGe("setCamera(): Error: arCreateHandle.\n"); + return -1; + } + // AR_DEFAULT_PIXEL_FORMAT + int set = arSetPixelFormat(this->arhandle, this->pixFormat); + + this->ar3DHandle = ar3DCreateHandle(&(this->param)); + if (this->ar3DHandle == NULL) { + ARLOGe("setCamera(): Error creating 3D handle\n"); + return -1; + } + + arglCameraFrustumRH(&((this->paramLT)->param), this->nearPlane, + this->farPlane, this->cameraLens); + + this->kpmHandle = createKpmHandle(this->paramLT); + + return 0; +} + +int ARToolKitNFT::loadCamera(std::string cparam_name) { + ARParam param; + if (arParamLoad(cparam_name.c_str(), 1, ¶m) < 0) { + ARLOGe("loadCamera(): Error loading parameter file %s for camera.\n", + cparam_name.c_str()); + return -1; + } + int cameraID = gCameraID++; + cameraParams[cameraID] = param; + + return cameraID; +} + +py::array_t ARToolKitNFT::getCameraLens() { + py::array_t lens({16}); + auto lens_ptr = lens.mutable_data(); + for (int i = 0; i < 16; i++) { + lens_ptr[i] = this->cameraLens[i]; + } + return lens; +} + +/*int ARToolKitNFT::decompressZFT(std::string datasetPathname, std::string tempPathname){ + int response = decompressMarkers(datasetPathname.c_str(), tempPathname.c_str()); + + return 1; +}*/ + +/***************** + * Marker loading * + *****************/ + +std::vector +ARToolKitNFT::addNFTMarkers(std::vector &datasetPathnames) { + + KpmHandle *kpmHandle = this->kpmHandle; + + KpmRefDataSet *refDataSet; + refDataSet = NULL; + + if (datasetPathnames.size() >= PAGES_MAX) { + ARLOGe("Error exceed maximum pages.\n"); + exit(-1); + } + + std::vector markerIds = {}; + + for (int i = 0; i < datasetPathnames.size(); i++) { + ARLOGi("datasetPathnames size: %i\n", datasetPathnames.size()); + ARLOGi("add NFT marker-> '%s'\n", datasetPathnames[i].c_str()); + + const char *datasetPathname = datasetPathnames[i].c_str(); + int pageNo = i; + markerIds.push_back(i); + + // Load KPM data. + KpmRefDataSet *refDataSet2; + ARLOGi("Reading %s.fset3\n", datasetPathname); + if (kpmLoadRefDataSet(datasetPathname, "fset3", &refDataSet2) < 0) { + ARLOGe("Error reading KPM data from %s.fset3\n", datasetPathname); + return {}; + } + ARLOGi("Assigned page no. %d.\n", pageNo); + if (kpmChangePageNoOfRefDataSet(refDataSet2, KpmChangePageNoAllPages, + pageNo) < 0) { + ARLOGe("Error: kpmChangePageNoOfRefDataSet\n"); + return {}; + } + if (kpmMergeRefDataSet(&refDataSet, &refDataSet2) < 0) { + ARLOGe("Error: kpmMergeRefDataSet\n"); + return {}; + } + ARLOGi("Done.\n"); + + // Load AR2 data. + ARLOGi("Reading %s.fset\n", datasetPathname); + + if ((this->surfaceSet[i] = + ar2ReadSurfaceSet(datasetPathname, "fset", NULL)) == NULL) { + ARLOGe("Error reading data from %s.fset\n", datasetPathname); + return {}; + } + + int surfaceSetCount = this->surfaceSetCount; + int numIset = this->surfaceSet[i]->surface[0].imageSet->num; + this->nft.width_NFT = + this->surfaceSet[i]->surface[0].imageSet->scale[0]->xsize; + this->nft.height_NFT = + this->surfaceSet[i]->surface[0].imageSet->scale[0]->ysize; + this->nft.dpi_NFT = this->surfaceSet[i]->surface[0].imageSet->scale[0]->dpi; + + ARLOGi("NFT num. of ImageSet: %i\n", numIset); + ARLOGi("NFT marker width: %i\n", this->nft.width_NFT); + ARLOGi("NFT marker height: %i\n", this->nft.height_NFT); + ARLOGi("NFT marker dpi: %i\n", this->nft.dpi_NFT); + + this->nft.id_NFT = i; + this->nft.width_NFT = this->nft.width_NFT; + this->nft.height_NFT = this->nft.height_NFT; + this->nft.dpi_NFT = this->nft.dpi_NFT; + this->nftMarkers.push_back(this->nft); + + ARLOGi("Done.\n"); + surfaceSetCount++; + } + + if (kpmSetRefDataSet(kpmHandle, refDataSet) < 0) { + ARLOGe("Error: kpmSetRefDataSet\n"); + return {}; + } + kpmDeleteRefDataSet(&refDataSet); + + ARLOGi("Loading of NFT data complete.\n"); + + this->surfaceSetCount += markerIds.size(); + + return markerIds; +} + +/********************** + * Setters and getters * + **********************/ + +/*************** + * Set Log Level + ****************/ +void ARToolKitNFT::setLogLevel(int level) { this->arLogLevel = level; } + +int ARToolKitNFT::getLogLevel() { return this->arLogLevel; } + +void ARToolKitNFT::setProjectionNearPlane(const ARdouble projectionNearPlane) { + this->nearPlane = projectionNearPlane; +} + +ARdouble ARToolKitNFT::getProjectionNearPlane() { return this->nearPlane; } + +void ARToolKitNFT::setProjectionFarPlane(const ARdouble projectionFarPlane) { + this->farPlane = projectionFarPlane; +} + +ARdouble ARToolKitNFT::getProjectionFarPlane() { return this->farPlane; } + +void ARToolKitNFT::setThreshold(int threshold) { + if (threshold < 0 || threshold > 255) + return; + if (arSetLabelingThresh(this->arhandle, threshold) == 0) { + ARLOGi("Threshold set to %d\n", threshold); + }; + // default 100 + // arSetLabelingThreshMode + // AR_LABELING_THRESH_MODE_MANUAL, AR_LABELING_THRESH_MODE_AUTO_MEDIAN, + // AR_LABELING_THRESH_MODE_AUTO_OTSU, AR_LABELING_THRESH_MODE_AUTO_ADAPTIVE +} + +int ARToolKitNFT::getThreshold() { + int threshold; + if (arGetLabelingThresh(this->arhandle, &threshold) == 0) { + return threshold; + }; + + return -1; +} + +void ARToolKitNFT::setThresholdMode(int mode) { + AR_LABELING_THRESH_MODE thresholdMode = (AR_LABELING_THRESH_MODE)mode; + + if (arSetLabelingThreshMode(this->arhandle, thresholdMode) == 0) { + ARLOGi("Threshold mode set to %d\n", (int)thresholdMode); + } +} + +int ARToolKitNFT::getThresholdMode() { + AR_LABELING_THRESH_MODE thresholdMode; + + if (arGetLabelingThreshMode(this->arhandle, &thresholdMode) == 0) { + return thresholdMode; + } + + return -1; +} + +int ARToolKitNFT::setDebugMode(int enable) { + arSetDebugMode(this->arhandle, enable ? AR_DEBUG_ENABLE : AR_DEBUG_DISABLE); + ARLOGi("Debug mode set to %s\n", enable ? "on." : "off."); + + return enable; +} + +int ARToolKitNFT::getProcessingImage() { + + return reinterpret_cast(this->arhandle->labelInfo.bwImage); +} + +int ARToolKitNFT::getDebugMode() { + int enable; + + arGetDebugMode(this->arhandle, &enable); + return enable; +} + +void ARToolKitNFT::setImageProcMode(int mode) { + + int imageProcMode = mode; + if (arSetImageProcMode(this->arhandle, mode) == 0) { + ARLOGi("Image proc. mode set to %d.\n", imageProcMode); + } +} + +int ARToolKitNFT::getImageProcMode() { + int imageProcMode; + if (arGetImageProcMode(this->arhandle, &imageProcMode) == 0) { + return imageProcMode; + } + + return -1; +} + +int ARToolKitNFT::setup(int width, int height, int cameraID) { + int id = gARControllerID++; + this->id = id; + + this->width = width; + this->height = height; + + this->videoFrameSize = width * height * 4 * sizeof(ARUint8); + this->videoFrame = (ARUint8 *)malloc(this->videoFrameSize); + this->videoLuma = (ARUint8 *)malloc(this->videoFrameSize / 4); + + setCamera(id, cameraID); + + ARLOGi("Allocated videoFrameSize %d\n", this->videoFrameSize); + + return this->id; +} + +PYBIND11_MODULE(jsartoolkitNFT, m) { + py::class_(m, "nftMarker") + .def(py::init<>()) + .def_readwrite("id_NFT", &nftMarker::id_NFT) + .def_readwrite("width_NFT", &nftMarker::width_NFT) + .def_readwrite("height_NFT", &nftMarker::height_NFT) + .def_readwrite("dpi_NFT", &nftMarker::dpi_NFT) + .def("__repr__", + [](const nftMarker &n) { + return ""; + }); + + + py::class_(m, "ARToolKitNFT") + .def(py::init<>()) + .def("passVideoData", &ARToolKitNFT::passVideoData) + .def("detectNFTMarker", &ARToolKitNFT::detectNFTMarker) + .def("getNFTMarkerInfo", &ARToolKitNFT::getNFTMarkerInfo) + //.def("getKpmImageWidth", &ARToolKitNFT::getKpmImageWidth) + //.def("getKpmImageHeight", &ARToolKitNFT::getKpmImageHeight) + .def("setupAR2", &ARToolKitNFT::setupAR2) + .def("getNFTData", &ARToolKitNFT::getNFTData) + .def("setLogLevel", &ARToolKitNFT::setLogLevel) + .def("getLogLevel", &ARToolKitNFT::getLogLevel) + .def("teardown", &ARToolKitNFT::teardown) + .def("loadCamera", &ARToolKitNFT::loadCamera) + .def("getCameraLens", &ARToolKitNFT::getCameraLens) + .def("addNFTMarkers", &ARToolKitNFT::addNFTMarkers) + .def("setProjectionNearPlane", &ARToolKitNFT::setProjectionNearPlane) + .def("getProjectionNearPlane", &ARToolKitNFT::getProjectionNearPlane) + .def("setProjectionFarPlane", &ARToolKitNFT::setProjectionFarPlane) + .def("getProjectionFarPlane", &ARToolKitNFT::getProjectionFarPlane) + .def("setThreshold", &ARToolKitNFT::setThreshold) + .def("getThreshold", &ARToolKitNFT::getThreshold) + .def("setThresholdMode", &ARToolKitNFT::setThresholdMode) + .def("getThresholdMode", &ARToolKitNFT::getThresholdMode) + .def("setDebugMode", &ARToolKitNFT::setDebugMode) + .def("getProcessingImage", &ARToolKitNFT::getProcessingImage) + .def("getDebugMode", &ARToolKitNFT::getDebugMode) + .def("setImageProcMode", &ARToolKitNFT::setImageProcMode) + .def("getImageProcMode", &ARToolKitNFT::getImageProcMode) + .def("setup", &ARToolKitNFT::setup); +} diff --git a/python-bindings/ARToolKitNFT_py.h b/python-bindings/ARToolKitNFT_py.h new file mode 100644 index 00000000..b04197cc --- /dev/null +++ b/python-bindings/ARToolKitNFT_py.h @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include "trackingMod.h" +//#include "markerDecompress.h" + +namespace py = pybind11; +using namespace pybind11::literals; + +const int PAGES_MAX = 20; // Maximum number of pages expected. You can change this down (to save memory) or up (to accomodate more pages.) + +struct nftMarker +{ + int id_NFT; + int width_NFT; + int height_NFT; + int dpi_NFT; +}; + +static int gARControllerID = 0; +static int gCameraID = 0; + +static int MARKER_INDEX_OUT_OF_BOUNDS = -3; + +std::unordered_map cameraParams; + +class ARToolKitNFT +{ +public: + ARToolKitNFT(); + ~ARToolKitNFT(); + int passVideoData(py::array_t videoFrame, py::array_t videoLuma); + py::dict getNFTMarkerInfo(int markerIndex); + + int detectNFTMarker(); + //int getKpmImageWidth(KpmHandle *kpmHandle); + //int getKpmImageHeight(KpmHandle *kpmHandle); + int setupAR2(); + nftMarker getNFTData(int index); + + void setLogLevel(int level); + int getLogLevel(); + + int teardown(); + int loadCamera(std::string cparam_name); + int setCamera(int id, int cameraID); + //emscripten::val getCameraLens(); + py::array_t getCameraLens(); + //int decompressZFT(std::string datasetPathname, std::string tempPathname); + std::vector addNFTMarkers(std::vector &datasetPathnames); + + // setters and getters + void setProjectionNearPlane(const ARdouble projectionNearPlane); + ARdouble getProjectionNearPlane(); + void setProjectionFarPlane(const ARdouble projectionFarPlane); + ARdouble getProjectionFarPlane(); + void setThreshold(int threshold); + int getThreshold(); + void setThresholdMode(int mode); + int getThresholdMode(); + int setDebugMode(int enable); + int getProcessingImage(); + int getDebugMode(); + void setImageProcMode(int mode); + int getImageProcMode(); + int setup(int width, int height, int cameraID); + +private: + KpmHandle *createKpmHandle(ARParamLT *cparamLT); + void deleteHandle(); + + int id; + + ARParam param; + ARParamLT *paramLT; + + ARUint8 *videoFrame; + int videoFrameSize; + ARUint8 *videoLuma; + + int width; + int height; + + ARHandle *arhandle; + AR3DHandle *ar3DHandle; + + KpmHandle *kpmHandle; + AR2HandleT *ar2Handle; + +#if WITH_FILTERING + ARFilterTransMatInfo *ftmi; + ARdouble filterCutoffFrequency; + ARdouble filterSampleRate; +#endif + + int arLogLevel = AR_LOG_LEVEL_INFO; + int detectedPage; + + int surfaceSetCount; + AR2SurfaceSetT *surfaceSet[PAGES_MAX]; + std::unordered_map surfaceSets; + // nftMarker struct inside arController + nftMarker nft; + std::vector nftMarkers; + + ARdouble nearPlane; + ARdouble farPlane; + + int patt_id; + + ARdouble cameraLens[16]; + AR_PIXEL_FORMAT pixFormat = AR_PIXEL_FORMAT_RGBA; +}; \ No newline at end of file diff --git a/python-bindings/deps/include/jconfig.h b/python-bindings/deps/include/jconfig.h new file mode 100644 index 00000000..5486de08 --- /dev/null +++ b/python-bindings/deps/include/jconfig.h @@ -0,0 +1,33 @@ +#define JPEG_LIB_VERSION 62 +#define LIBJPEG_TURBO_VERSION 2.1.3 +#define LIBJPEG_TURBO_VERSION_NUMBER 2001003 + +#define C_ARITH_CODING_SUPPORTED +#define D_ARITH_CODING_SUPPORTED +/* #undef MEM_SRCDST_SUPPORTED */ +#define WITH_SIMD + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_SYS_TYPES_H +#undef NEED_BSD_STRINGS + +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +#undef INCOMPLETE_TYPES_BROKEN +#undef RIGHT_SHIFT_IS_UNSIGNED + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ + +/* Define "INT32" as int, not long, per Windows custom */ +#if !(defined(_BASETSD_H_) || defined(_BASETSD_H)) /* don't conflict if basetsd.h already read */ +typedef short INT16; +typedef signed int INT32; +#endif +#define XMD_H /* prevent jmorecfg.h from redefining it */ diff --git a/python-bindings/deps/include/jmorecfg.h b/python-bindings/deps/include/jmorecfg.h new file mode 100644 index 00000000..b33a9919 --- /dev/null +++ b/python-bindings/deps/include/jmorecfg.h @@ -0,0 +1,382 @@ +/* + * jmorecfg.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2009, 2011, 2014-2015, 2018, 2020, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of Rec. ITU-T T.81 | ISO/IEC 10918-1, set this to 255. + * However, darn few applications need more than 4 channels (maybe 5 for CMYK + + * alpha mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + */ + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int)(value)) + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int)(value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +typedef unsigned char UINT8; + +/* UINT16 must hold at least the values 0..65535. */ + +typedef unsigned short UINT16; + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. + * + * NOTE: The INT32 typedef dates back to libjpeg v5 (1994.) Integers were + * sometimes 16-bit back then (MS-DOS), which is why INT32 is typedef'd to + * long. It also wasn't common (or at least as common) in 1994 for INT32 to be + * defined by platform headers. Since then, however, INT32 is defined in + * several other common places: + * + * Xmd.h (X11 header) typedefs INT32 to int on 64-bit platforms and long on + * 32-bit platforms (i.e always a 32-bit signed type.) + * + * basetsd.h (Win32 header) typedefs INT32 to int (always a 32-bit signed type + * on modern platforms.) + * + * qglobal.h (Qt header) typedefs INT32 to int (always a 32-bit signed type on + * modern platforms.) + * + * This is a recipe for conflict, since "long" and "int" aren't always + * compatible types. Since the definition of INT32 has technically been part + * of the libjpeg API for more than 20 years, we can't remove it, but we do not + * use it internally any longer. We instead define a separate type (JLONG) + * for internal use, which ensures that internal behavior will always be the + * same regardless of any external headers that may be included. + */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. (Note that changing this datatype will + * potentially require modifying the SIMD code. The x86-64 SIMD extensions, + * in particular, assume a 32-bit JDIMENSION.) + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* Originally, this macro was used as a way of defining function prototypes + * for both modern compilers as well as older compilers that did not support + * prototype parameters. libjpeg-turbo has never supported these older, + * non-ANSI compilers, but the macro is still included because there is some + * software out there that uses it. + */ + +#define JMETHOD(type, methodname, arglist) type (*methodname) arglist + + +/* libjpeg-turbo no longer supports platforms that have far symbols (MS-DOS), + * but again, some software relies on this macro. + */ + +#undef FAR +#define FAR + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* accurate integer method */ +#define DCT_IFAST_SUPPORTED /* less accurate int method [legacy feature] */ +#define DCT_FLOAT_SUPPORTED /* floating-point method [legacy feature] */ + +/* Encoder capability options: */ + +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * The RGB_RED, RGB_GREEN, RGB_BLUE, and RGB_PIXELSIZE macros are a vestigial + * feature of libjpeg. The idea was that, if an application developer needed + * to compress from/decompress to a BGR/BGRX/RGBX/XBGR/XRGB buffer, they could + * change these macros, rebuild libjpeg, and link their application statically + * with it. In reality, few people ever did this, because there were some + * severe restrictions involved (cjpeg and djpeg no longer worked properly, + * compressing/decompressing RGB JPEGs no longer worked properly, and the color + * quantizer wouldn't work with pixel sizes other than 3.) Furthermore, since + * all of the O/S-supplied versions of libjpeg were built with the default + * values of RGB_RED, RGB_GREEN, RGB_BLUE, and RGB_PIXELSIZE, many applications + * have come to regard these values as immutable. + * + * The libjpeg-turbo colorspace extensions provide a much cleaner way of + * compressing from/decompressing to buffers with arbitrary component orders + * and pixel sizes. Thus, we do not support changing the values of RGB_RED, + * RGB_GREEN, RGB_BLUE, or RGB_PIXELSIZE. In addition to the restrictions + * listed above, changing these values will also break the SIMD extensions and + * the regression tests. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + +#define JPEG_NUMCS 17 + +#define EXT_RGB_RED 0 +#define EXT_RGB_GREEN 1 +#define EXT_RGB_BLUE 2 +#define EXT_RGB_PIXELSIZE 3 + +#define EXT_RGBX_RED 0 +#define EXT_RGBX_GREEN 1 +#define EXT_RGBX_BLUE 2 +#define EXT_RGBX_PIXELSIZE 4 + +#define EXT_BGR_RED 2 +#define EXT_BGR_GREEN 1 +#define EXT_BGR_BLUE 0 +#define EXT_BGR_PIXELSIZE 3 + +#define EXT_BGRX_RED 2 +#define EXT_BGRX_GREEN 1 +#define EXT_BGRX_BLUE 0 +#define EXT_BGRX_PIXELSIZE 4 + +#define EXT_XBGR_RED 3 +#define EXT_XBGR_GREEN 2 +#define EXT_XBGR_BLUE 1 +#define EXT_XBGR_PIXELSIZE 4 + +#define EXT_XRGB_RED 1 +#define EXT_XRGB_GREEN 2 +#define EXT_XRGB_BLUE 3 +#define EXT_XRGB_PIXELSIZE 4 + +static const int rgb_red[JPEG_NUMCS] = { + -1, -1, RGB_RED, -1, -1, -1, EXT_RGB_RED, EXT_RGBX_RED, + EXT_BGR_RED, EXT_BGRX_RED, EXT_XBGR_RED, EXT_XRGB_RED, + EXT_RGBX_RED, EXT_BGRX_RED, EXT_XBGR_RED, EXT_XRGB_RED, + -1 +}; + +static const int rgb_green[JPEG_NUMCS] = { + -1, -1, RGB_GREEN, -1, -1, -1, EXT_RGB_GREEN, EXT_RGBX_GREEN, + EXT_BGR_GREEN, EXT_BGRX_GREEN, EXT_XBGR_GREEN, EXT_XRGB_GREEN, + EXT_RGBX_GREEN, EXT_BGRX_GREEN, EXT_XBGR_GREEN, EXT_XRGB_GREEN, + -1 +}; + +static const int rgb_blue[JPEG_NUMCS] = { + -1, -1, RGB_BLUE, -1, -1, -1, EXT_RGB_BLUE, EXT_RGBX_BLUE, + EXT_BGR_BLUE, EXT_BGRX_BLUE, EXT_XBGR_BLUE, EXT_XRGB_BLUE, + EXT_RGBX_BLUE, EXT_BGRX_BLUE, EXT_XBGR_BLUE, EXT_XRGB_BLUE, + -1 +}; + +static const int rgb_pixelsize[JPEG_NUMCS] = { + -1, -1, RGB_PIXELSIZE, -1, -1, -1, EXT_RGB_PIXELSIZE, EXT_RGBX_PIXELSIZE, + EXT_BGR_PIXELSIZE, EXT_BGRX_PIXELSIZE, EXT_XBGR_PIXELSIZE, EXT_XRGB_PIXELSIZE, + EXT_RGBX_PIXELSIZE, EXT_BGRX_PIXELSIZE, EXT_XBGR_PIXELSIZE, EXT_XRGB_PIXELSIZE, + -1 +}; + +/* Definitions for speed-related optimizations. */ + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#ifndef WITH_SIMD +#define MULTIPLIER int /* type for fastest integer multiply */ +#else +#define MULTIPLIER short /* prefer 16-bit with SIMD for parellelism */ +#endif +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + */ + +#ifndef FAST_FLOAT +#define FAST_FLOAT float +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/python-bindings/deps/include/jpeglib.h b/python-bindings/deps/include/jpeglib.h new file mode 100644 index 00000000..d7664f06 --- /dev/null +++ b/python-bindings/deps/include/jpeglib.h @@ -0,0 +1,1132 @@ +/* + * jpeglib.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2009 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2009-2011, 2013-2014, 2016-2017, 2020, D. R. Commander. + * Copyright (C) 2015, Google, Inc. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + */ + +typedef JSAMPLE *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different IDCT scalings. + */ +#if JPEG_LIB_VERSION >= 70 + int DCT_h_scaled_size; + int DCT_v_scaled_size; +#else + int DCT_scaled_size; +#endif + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_[h_]scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_[h_]scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL *quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void *dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct *jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET *data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +#define JCS_EXTENSIONS 1 +#define JCS_ALPHA_EXTENSIONS 1 + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue as specified by the RGB_RED, + RGB_GREEN, RGB_BLUE, and RGB_PIXELSIZE macros */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK, /* Y/Cb/Cr/K */ + JCS_EXT_RGB, /* red/green/blue */ + JCS_EXT_RGBX, /* red/green/blue/x */ + JCS_EXT_BGR, /* blue/green/red */ + JCS_EXT_BGRX, /* blue/green/red/x */ + JCS_EXT_XBGR, /* x/blue/green/red */ + JCS_EXT_XRGB, /* x/red/green/blue */ + /* When out_color_space it set to JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR, + or JCS_EXT_XRGB during decompression, the X byte is undefined, and in + order to ensure the best performance, libjpeg-turbo can set that byte to + whatever value it wishes. Use the following colorspace constants to + ensure that the X byte is set to 0xFF, so that it can be interpreted as an + opaque alpha channel. */ + JCS_EXT_RGBA, /* red/green/blue/alpha */ + JCS_EXT_BGRA, /* blue/green/red/alpha */ + JCS_EXT_ABGR, /* alpha/blue/green/red */ + JCS_EXT_ARGB, /* alpha/red/green/blue */ + JCS_RGB565 /* 5-bit red/6-bit green/5-bit blue */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* accurate integer method */ + JDCT_IFAST, /* less accurate integer method [legacy feature] */ + JDCT_FLOAT /* floating-point method [legacy feature] */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr *err; /* Error handler module */ \ + struct jpeg_memory_mgr *mem; /* Memory manager module */ \ + struct jpeg_progress_mgr *progress; /* Progress monitor, or NULL if none */ \ + void *client_data; /* Available for use by application */ \ + boolean is_decompressor; /* So common code can tell which is which */ \ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct *j_common_ptr; +typedef struct jpeg_compress_struct *j_compress_ptr; +typedef struct jpeg_decompress_struct *j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr *dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + +#if JPEG_LIB_VERSION >= 70 + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ +#endif + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info *comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; +#if JPEG_LIB_VERSION >= 70 + int q_scale_factor[NUM_QUANT_TBLS]; +#endif + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info *scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ +#if JPEG_LIB_VERSION >= 70 + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ +#endif + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + +#if JPEG_LIB_VERSION >= 70 + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ +#endif + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + +#if JPEG_LIB_VERSION >= 80 + int block_size; /* the basic DCT block size: 1..16 */ + const int *natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ +#endif + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master *master; + struct jpeg_c_main_controller *main; + struct jpeg_c_prep_controller *prep; + struct jpeg_c_coef_controller *coef; + struct jpeg_marker_writer *marker; + struct jpeg_color_converter *cconvert; + struct jpeg_downsampler *downsample; + struct jpeg_forward_dct *fdct; + struct jpeg_entropy_encoder *entropy; + jpeg_scan_info *script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr *src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info *comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + +#if JPEG_LIB_VERSION >= 80 + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ +#endif + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + +#if JPEG_LIB_VERSION >= 70 + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ +#else + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ +#endif + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_[v_]scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE *sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + +#if JPEG_LIB_VERSION >= 80 + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int *natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ +#endif + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master *master; + struct jpeg_d_main_controller *main; + struct jpeg_d_coef_controller *coef; + struct jpeg_d_post_controller *post; + struct jpeg_input_controller *inputctl; + struct jpeg_marker_reader *marker; + struct jpeg_entropy_decoder *entropy; + struct jpeg_inverse_dct *idct; + struct jpeg_upsampler *upsample; + struct jpeg_color_deconverter *cconvert; + struct jpeg_color_quantizer *cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + void (*error_exit) (j_common_ptr cinfo); + /* Conditionally emit a trace or warning message */ + void (*emit_message) (j_common_ptr cinfo, int msg_level); + /* Routine that actually outputs a trace or error message */ + void (*output_message) (j_common_ptr cinfo); + /* Format a message string for the most recent JPEG error or message */ + void (*format_message) (j_common_ptr cinfo, char *buffer); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + void (*reset_error_mgr) (j_common_ptr cinfo); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const *jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const *addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + void (*progress_monitor) (j_common_ptr cinfo); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET *next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + void (*init_destination) (j_compress_ptr cinfo); + boolean (*empty_output_buffer) (j_compress_ptr cinfo); + void (*term_destination) (j_compress_ptr cinfo); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET *next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + void (*init_source) (j_decompress_ptr cinfo); + boolean (*fill_input_buffer) (j_decompress_ptr cinfo); + void (*skip_input_data) (j_decompress_ptr cinfo, long num_bytes); + boolean (*resync_to_restart) (j_decompress_ptr cinfo, int desired); + void (*term_source) (j_decompress_ptr cinfo); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control *jvirt_sarray_ptr; +typedef struct jvirt_barray_control *jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + void *(*alloc_small) (j_common_ptr cinfo, int pool_id, size_t sizeofobject); + void *(*alloc_large) (j_common_ptr cinfo, int pool_id, + size_t sizeofobject); + JSAMPARRAY (*alloc_sarray) (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows); + JBLOCKARRAY (*alloc_barray) (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows); + jvirt_sarray_ptr (*request_virt_sarray) (j_common_ptr cinfo, int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess); + jvirt_barray_ptr (*request_virt_barray) (j_common_ptr cinfo, int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess); + void (*realize_virt_arrays) (j_common_ptr cinfo); + JSAMPARRAY (*access_virt_sarray) (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable); + JBLOCKARRAY (*access_virt_barray) (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable); + void (*free_pool) (j_common_ptr cinfo, int pool_id); + void (*self_destruct) (j_common_ptr cinfo); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef boolean (*jpeg_marker_parser_method) (j_decompress_ptr cinfo); + + +/* Originally, this macro was used as a way of defining function prototypes + * for both modern compilers as well as older compilers that did not support + * prototype parameters. libjpeg-turbo has never supported these older, + * non-ANSI compilers, but the macro is still included because there is some + * software out there that uses it. + */ + +#define JPP(arglist) arglist + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error(struct jpeg_error_mgr *err); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t)sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t)sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress(j_compress_ptr cinfo, int version, + size_t structsize); +EXTERN(void) jpeg_CreateDecompress(j_decompress_ptr cinfo, int version, + size_t structsize); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress(j_compress_ptr cinfo); +EXTERN(void) jpeg_destroy_decompress(j_decompress_ptr cinfo); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest(j_compress_ptr cinfo, FILE *outfile); +EXTERN(void) jpeg_stdio_src(j_decompress_ptr cinfo, FILE *infile); + +#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest(j_compress_ptr cinfo, unsigned char **outbuffer, + unsigned long *outsize); +EXTERN(void) jpeg_mem_src(j_decompress_ptr cinfo, + const unsigned char *inbuffer, unsigned long insize); +#endif + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults(j_compress_ptr cinfo); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace(j_compress_ptr cinfo, + J_COLOR_SPACE colorspace); +EXTERN(void) jpeg_default_colorspace(j_compress_ptr cinfo); +EXTERN(void) jpeg_set_quality(j_compress_ptr cinfo, int quality, + boolean force_baseline); +EXTERN(void) jpeg_set_linear_quality(j_compress_ptr cinfo, int scale_factor, + boolean force_baseline); +#if JPEG_LIB_VERSION >= 70 +EXTERN(void) jpeg_default_qtables(j_compress_ptr cinfo, + boolean force_baseline); +#endif +EXTERN(void) jpeg_add_quant_table(j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline); +EXTERN(int) jpeg_quality_scaling(int quality); +EXTERN(void) jpeg_simple_progression(j_compress_ptr cinfo); +EXTERN(void) jpeg_suppress_tables(j_compress_ptr cinfo, boolean suppress); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table(j_common_ptr cinfo); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table(j_common_ptr cinfo); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress(j_compress_ptr cinfo, + boolean write_all_tables); +EXTERN(JDIMENSION) jpeg_write_scanlines(j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines); +EXTERN(void) jpeg_finish_compress(j_compress_ptr cinfo); + +#if JPEG_LIB_VERSION >= 70 +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions(j_compress_ptr cinfo); +#endif + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data(j_compress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION num_lines); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker(j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header(j_compress_ptr cinfo, int marker, + unsigned int datalen); +EXTERN(void) jpeg_write_m_byte(j_compress_ptr cinfo, int val); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables(j_compress_ptr cinfo); + +/* Write ICC profile. See libjpeg.txt for usage information. */ +EXTERN(void) jpeg_write_icc_profile(j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len); + + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header(j_decompress_ptr cinfo, boolean require_image); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress(j_decompress_ptr cinfo); +EXTERN(JDIMENSION) jpeg_read_scanlines(j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines); +EXTERN(JDIMENSION) jpeg_skip_scanlines(j_decompress_ptr cinfo, + JDIMENSION num_lines); +EXTERN(void) jpeg_crop_scanline(j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width); +EXTERN(boolean) jpeg_finish_decompress(j_decompress_ptr cinfo); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans(j_decompress_ptr cinfo); +EXTERN(boolean) jpeg_start_output(j_decompress_ptr cinfo, int scan_number); +EXTERN(boolean) jpeg_finish_output(j_decompress_ptr cinfo); +EXTERN(boolean) jpeg_input_complete(j_decompress_ptr cinfo); +EXTERN(void) jpeg_new_colormap(j_decompress_ptr cinfo); +EXTERN(int) jpeg_consume_input(j_decompress_ptr cinfo); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +#if JPEG_LIB_VERSION >= 80 +EXTERN(void) jpeg_core_output_dimensions(j_decompress_ptr cinfo); +#endif +EXTERN(void) jpeg_calc_output_dimensions(j_decompress_ptr cinfo); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers(j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor(j_decompress_ptr cinfo, + int marker_code, + jpeg_marker_parser_method routine); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients(j_decompress_ptr cinfo); +EXTERN(void) jpeg_write_coefficients(j_compress_ptr cinfo, + jvirt_barray_ptr *coef_arrays); +EXTERN(void) jpeg_copy_critical_parameters(j_decompress_ptr srcinfo, + j_compress_ptr dstinfo); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress(j_compress_ptr cinfo); +EXTERN(void) jpeg_abort_decompress(j_decompress_ptr cinfo); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort(j_common_ptr cinfo); +EXTERN(void) jpeg_destroy(j_common_ptr cinfo); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart(j_decompress_ptr cinfo, int desired); + +/* Read ICC profile. See libjpeg.txt for usage information. */ +EXTERN(boolean) jpeg_read_icc_profile(j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/python-bindings/deps/include/jversion.h b/python-bindings/deps/include/jversion.h new file mode 100644 index 00000000..2ab534af --- /dev/null +++ b/python-bindings/deps/include/jversion.h @@ -0,0 +1,54 @@ +/* + * jversion.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2010, 2012-2021, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains software version identification. + */ + + +#if JPEG_LIB_VERSION >= 80 + +#define JVERSION "8d 15-Jan-2012" + +#elif JPEG_LIB_VERSION >= 70 + +#define JVERSION "7 27-Jun-2009" + +#else + +#define JVERSION "6b 27-Mar-1998" + +#endif + +/* + * NOTE: It is our convention to place the authors in the following order: + * - libjpeg-turbo authors (2009-) in descending order of the date of their + * most recent contribution to the project, then in ascending order of the + * date of their first contribution to the project, then in alphabetical + * order + * - Upstream authors in descending order of the date of the first inclusion of + * their code + */ + +#define JCOPYRIGHT \ + "Copyright (C) 2009-2021 D. R. Commander\n" \ + "Copyright (C) 2015, 2020 Google, Inc.\n" \ + "Copyright (C) 2019-2020 Arm Limited\n" \ + "Copyright (C) 2015-2016, 2018 Matthieu Darbois\n" \ + "Copyright (C) 2011-2016 Siarhei Siamashka\n" \ + "Copyright (C) 2015 Intel Corporation\n" \ + "Copyright (C) 2013-2014 Linaro Limited\n" \ + "Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \ + "Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \ + "Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \ + "Copyright (C) 1999-2006 MIYASAKA Masaru\n" \ + "Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding" + +#define JCOPYRIGHT_SHORT \ + "Copyright (C) 1991-2021 The libjpeg-turbo Project and many others" diff --git a/python-bindings/deps/include/sched.h b/python-bindings/deps/include/sched.h new file mode 100644 index 00000000..f36a97a6 --- /dev/null +++ b/python-bindings/deps/include/sched.h @@ -0,0 +1,183 @@ +/* + * Module: sched.h + * + * Purpose: + * Provides an implementation of POSIX realtime extensions + * as defined in + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * -------------------------------------------------------------------------- + * + * Pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999,2005 Pthreads-win32 contributors + * + * Contact Email: rpj@callisto.canberra.edu.au + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(_SCHED_H) +#define _SCHED_H + +#undef PTW32_SCHED_LEVEL + +#if defined(_POSIX_SOURCE) +#define PTW32_SCHED_LEVEL 0 +/* Early POSIX */ +#endif + +#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309 +#undef PTW32_SCHED_LEVEL +#define PTW32_SCHED_LEVEL 1 +/* Include 1b, 1c and 1d */ +#endif + +#if defined(INCLUDE_NP) +#undef PTW32_SCHED_LEVEL +#define PTW32_SCHED_LEVEL 2 +/* Include Non-Portable extensions */ +#endif + +#define PTW32_SCHED_LEVEL_MAX 3 + +#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 ) || !defined(PTW32_SCHED_LEVEL) +#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX +/* Include everything */ +#endif + + +#if defined(__GNUC__) && !defined(__declspec) +# error Please upgrade your GNU compiler to one that supports __declspec. +#endif + +/* + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, + * do NOT define PTW32_BUILD, and then the variables/functions will + * be imported correctly. + */ +#if !defined(PTW32_STATIC_LIB) +# if defined(PTW32_BUILD) +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif +#else +# define PTW32_DLLPORT +#endif + +/* + * This is a duplicate of what is in the autoconf config.h, + * which is only used when building the pthread-win32 libraries. + */ + +#if !defined(PTW32_CONFIG_H) +# if defined(WINCE) +# define NEED_ERRNO +# define NEED_SEM +# endif +# if defined(__MINGW64__) +# define HAVE_STRUCT_TIMESPEC +# define HAVE_MODE_T +# elif defined(_UWIN) || defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +/* + * + */ + +#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX +#if defined(NEED_ERRNO) +#include "need_errno.h" +#else +#include +#endif +#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */ + +#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN) +# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX +/* For pid_t */ +# include +/* Required by Unix 98 */ +# include +# else + typedef int pid_t; +# endif +#else + typedef int pid_t; +#endif + +/* Thread scheduling policies */ + +enum { + SCHED_OTHER = 0, + SCHED_FIFO, + SCHED_RR, + SCHED_MIN = SCHED_OTHER, + SCHED_MAX = SCHED_RR +}; + +struct sched_param { + int sched_priority; +}; + +#if defined(__cplusplus) +extern "C" +{ +#endif /* __cplusplus */ + +PTW32_DLLPORT int __cdecl sched_yield (void); + +PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy); + +PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy); + +PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy); + +PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid); + +/* + * Note that this macro returns ENOTSUP rather than + * ENOSYS as might be expected. However, returning ENOSYS + * should mean that sched_get_priority_{min,max} are + * not implemented as well as sched_rr_get_interval. + * This is not the case, since we just don't support + * round-robin scheduling. Therefore I have chosen to + * return the same value as sched_setscheduler when + * SCHED_RR is passed to it. + */ +#define sched_rr_get_interval(_pid, _interval) \ + ( errno = ENOTSUP, (int) -1 ) + + +#if defined(__cplusplus) +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#undef PTW32_SCHED_LEVEL +#undef PTW32_SCHED_LEVEL_MAX + +#endif /* !_SCHED_H */ + diff --git a/python-bindings/deps/libs/libjpeg.lib b/python-bindings/deps/libs/libjpeg.lib new file mode 100644 index 00000000..4feda9f6 Binary files /dev/null and b/python-bindings/deps/libs/libjpeg.lib differ diff --git a/python-bindings/deps/libs/pthreadVC2static.lib b/python-bindings/deps/libs/pthreadVC2static.lib new file mode 100644 index 00000000..9d44f368 Binary files /dev/null and b/python-bindings/deps/libs/pthreadVC2static.lib differ diff --git a/python-bindings/deps/libs/zlib.lib b/python-bindings/deps/libs/zlib.lib new file mode 100644 index 00000000..41de7e99 Binary files /dev/null and b/python-bindings/deps/libs/zlib.lib differ diff --git a/python-bindings/setup.py b/python-bindings/setup.py new file mode 100644 index 00000000..ffe40749 --- /dev/null +++ b/python-bindings/setup.py @@ -0,0 +1,193 @@ +from glob import glob +from setuptools import setup, Extension +from pybind11.setup_helpers import Pybind11Extension, build_ext +import pybind11 +import os +import shutil +import subprocess +import sys + +LIBJPEG_VERSION = '9c' +LIBJPEG_URL = f'http://www.ijg.org/files/jpegsrc.v{LIBJPEG_VERSION}.tar.gz' +LIBJPEG_DIR = 'deps/libjpeg' +ZLIB_DIR = '../emscripten/zlib' +CONFIG_H_IN = os.path.join(os.path.dirname(__file__), '../emscripten/WebARKitLib/include/AR/config.h.in') +CONFIG_H = os.path.join(os.path.dirname(__file__), '../emscripten/WebARKitLib/include/AR/config.h') + +def download_and_extract(url, dest): + subprocess.run(['curl', '-L', url, '-o', 'libjpeg.tar.gz'], check=True) + subprocess.run(['tar', 'xzf', 'libjpeg.tar.gz'], check=True) + shutil.move(f'jpeg-{LIBJPEG_VERSION}', dest) + os.remove('libjpeg.tar.gz') + +def build_zlib(): + build_dir = os.path.join(ZLIB_DIR, 'build') + install_dir = os.path.join(ZLIB_DIR, 'install') + os.makedirs(build_dir, exist_ok=True) + os.makedirs(install_dir, exist_ok=True) + cmake_command = [ + 'cmake', + '-S', ZLIB_DIR, + '-B', build_dir, + '-DCMAKE_BUILD_TYPE=Release', + f'-DCMAKE_INSTALL_PREFIX={install_dir}' + ] + build_command = ['cmake', '--build', build_dir, '--config', 'Release'] + install_command = ['cmake', '--install', build_dir] + + try: + subprocess.run(cmake_command, check=True) + subprocess.run(build_command, check=True) + subprocess.run(install_command, check=True) + except subprocess.CalledProcessError as e: + print(f"Error building zlib: {e}") + sys.exit(1) + +def build_libjpeg(): + if sys.platform == 'win32': + # Use MSYS2 to build libjpeg on Windows + build_dir = os.path.abspath(os.path.join(LIBJPEG_DIR, 'build')) + os.makedirs(build_dir, exist_ok=True) + # Full path to MSYS2 + msys2_path = r'C:\msys64\usr\bin' # Adjust this path if necessary + os.environ['PATH'] = msys2_path + os.pathsep + os.environ['PATH'] + # Convert Windows path to Unix-style path using MSYS2 + try: + build_dir_unix = subprocess.check_output(['bash', '-c', f'cygpath -u {build_dir}']).strip().decode('utf-8') + print(f"Converted build directory: {build_dir_unix}") + except subprocess.CalledProcessError as e: + print(f"Error converting path: {e}") + sys.exit(1) + # Change the installation directory to a location with write permissions + install_dir = os.path.join(build_dir, 'install') + os.makedirs(install_dir, exist_ok=True) + install_dir_unix = subprocess.check_output(['bash', '-c', f'cygpath -u {install_dir}']).strip().decode('utf-8') + subprocess.run(['bash', '-c', f'./configure --prefix={install_dir_unix}'], cwd=LIBJPEG_DIR, check=True) + subprocess.run(['bash', '-c', 'make'], cwd=LIBJPEG_DIR, check=True) + subprocess.run(['bash', '-c', 'make install'], cwd=LIBJPEG_DIR, check=True) + else: + build_dir = os.path.abspath(os.path.join(LIBJPEG_DIR, 'build')) + os.makedirs(build_dir, exist_ok=True) + subprocess.run(['./configure', '--prefix=' + build_dir], cwd=LIBJPEG_DIR, check=True) + subprocess.run(['make'], cwd=LIBJPEG_DIR, check=True) + subprocess.run(['make', 'install'], cwd=LIBJPEG_DIR, check=True) + +def generate_config_h(): + with open(CONFIG_H_IN, 'r') as file: + config_h_content = file.read() + + config_h_content = config_h_content.replace('#undef ARVIDEO_INPUT_DEFAULT_DUMMY', + '#define ARVIDEO_INPUT_DEFAULT_DUMMY') + + with open(CONFIG_H, 'w') as file: + file.write(config_h_content) + +# Build zlib +build_zlib() + +# Check if the libjpeg directory exists, if not, download and extract it +if not os.path.exists(LIBJPEG_DIR): + download_and_extract(LIBJPEG_URL, LIBJPEG_DIR) + +# Build libjpeg +if sys.platform.startswith('linux'): + build_libjpeg() + +# Generate config.h from config.h.in +generate_config_h() + +# Windows-specific step to install pthread static library using vcpkg +if sys.platform == 'win32': + print("Running Windows-specific setup step to install pthread static library") + vcpkg_path = os.path.join(os.getcwd(), 'vcpkg') + if not os.path.exists(vcpkg_path): + subprocess.run(['git', 'clone', 'https://github.com/microsoft/vcpkg.git'], check=True) + subprocess.run([os.path.join(vcpkg_path, 'bootstrap-vcpkg.bat')], check=True) + # Install necessary tools using choco + subprocess.run(['choco', 'install', 'cmake', 'ninja', 'visualstudio2019buildtools', 'visualstudio2019-workload-vctools', '-y'], check=True) + subprocess.run([os.path.join(vcpkg_path, 'vcpkg'), 'install', 'pthreads:x64-windows-static'], check=True) + os.environ['VCPKG_ROOT'] = vcpkg_path + +# Sort the list of files +sorted_ar_files = sorted(glob('../emscripten/WebARKitLib/lib/SRC/AR/*.c')) +sorted_ar2_files = sorted(glob('../emscripten/WebARKitLib/lib/SRC/AR2/*.c')) +sorted_arutil_files = sorted(glob('../emscripten/WebARKitLib/lib/SRC/ARUtil/*.c')) +sorted_arLabeling_files = sorted(glob('../emscripten/WebARKitLib/lib/SRC/AR/arLabelingSub/*.c')) +sorted_aricp_files = sorted(glob('../emscripten/WebARKitLib/lib/SRC/ARICP/*.c')) + +include_dirs = [ + pybind11.get_include(), + '../emscripten', + '../emscripten/WebARKitLib/include', + '../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher' +] + +library_dirs = [] +libraries = [] +extra_compile_args = [] + +if sys.platform == 'win32': + include_dirs.extend([ + 'deps/include', + 'deps/libjpeg', + '../emscripten/zlib', + '../emscripten/zlib/build', + 'vcpkg/packages/pthreads_x64-windows-static/include', + os.path.join(os.getenv('VCPKG_ROOT', 'vcpkg'), 'installed', 'x64-windows', 'include') + ]) + library_dirs.extend([ + 'deps/libs', + '../emscripten/zlib/build/Release', + 'vcpkg/packages/pthreads_x64-windows-static/lib', + os.path.join(os.getenv('VCPKG_ROOT', 'vcpkg'), 'installed', 'x64-windows', 'lib') + ]) + libraries.extend(['zlib', 'libjpeg', 'Advapi32', 'Shell32', 'pthreadVC3', 'pthreadVC2static']) + extra_compile_args.extend(['/std:c++17', '/Dcpu_set_t=struct{unsigned long __bits[1024 / (8 * sizeof(unsigned long))];}']) # Set the C++ standard to C++17 and define cpu_set_t +else: + include_dirs.append(os.path.join(LIBJPEG_DIR, 'build', 'include')) + library_dirs.append(os.path.join(LIBJPEG_DIR, 'build', 'lib')) + libraries.extend(['z', 'm','jpeg']) + +ext_modules = [ + Pybind11Extension( + 'jsartoolkitNFT', + sources=sorted_ar_files + sorted_ar2_files + sorted_arutil_files + sorted_arLabeling_files + sorted_aricp_files + [ + 'ARToolKitNFT_py.cpp', + '../emscripten/trackingMod.c', + '../emscripten/trackingMod2d.c', + '../emscripten/WebARKitLib/lib/SRC/KPM/kpmFopen.c', + '../emscripten/WebARKitLib/lib/SRC/KPM/kpmHandle.cpp', + '../emscripten/WebARKitLib/lib/SRC/KPM/kpmMatching.cpp', + '../emscripten/WebARKitLib/lib/SRC/KPM/kpmRefDataSet.cpp', + '../emscripten/WebARKitLib/lib/SRC/KPM/kpmResult.cpp', + '../emscripten/WebARKitLib/lib/SRC/KPM/kpmUtil.cpp', + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/detectors/DoG_scale_invariant_detector.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/detectors/gaussian_scale_space_pyramid.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/detectors/gradients.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/detectors/orientation_assignment.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/detectors/pyramid.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/facade/visual_database_facade.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/matchers/hough_similarity_voting.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/matchers/freak.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/framework/date_time.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/framework/image.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/framework/logger.cpp", + "../emscripten/WebARKitLib/lib/SRC/KPM/FreakMatcher/framework/timers.cpp", + ], + include_dirs=include_dirs, + libraries=libraries, + library_dirs=library_dirs, + language='c++', + extra_compile_args=extra_compile_args + ), +] + +setup( + name='jsartoolkitNFT', + author='@kalwalt', + description='This is a Python binding project for jsartoolkitNFT, which integrates WebARKitLib with Python using pybind11. It allows for augmented reality applications to be developed in Python by providing bindings to the underlying C/C++ WebARKitLib library.', + ext_modules=ext_modules, + cmdclass={'build_ext': build_ext}, + zip_safe=False, + python_requires=">=3.7", +) \ No newline at end of file diff --git a/python-bindings/test.py b/python-bindings/test.py new file mode 100644 index 00000000..bc09373f --- /dev/null +++ b/python-bindings/test.py @@ -0,0 +1,24 @@ +import jsartoolkitNFT +import numpy as np + +nft = jsartoolkitNFT.ARToolKitNFT() +cameraId = nft.loadCamera('../examples/Data/camera_para.dat') +width = 640 +height = 480 +id = nft.setup(width, height, cameraId) +nft.setupAR2() +camera_mat = nft.getCameraLens() +print(camera_mat) +framesize = width * height +videoLuma = np.zeros((height, width, 1), dtype=np.uint8) + +nft.setProjectionNearPlane(0.1) +nft.setProjectionFarPlane(1000) + +marker = nft.addNFTMarkers(['../examples/DataNFT/pinball']) + +nftData = nft.getNFTData(id) +print(nftData) +print(f"Width: {nftData.width_NFT}") +print(f"Height: {nftData.height_NFT}") +print(f"DPI: {nftData.dpi_NFT}") \ No newline at end of file diff --git a/python-bindings/unittests.py b/python-bindings/unittests.py new file mode 100644 index 00000000..ba668105 --- /dev/null +++ b/python-bindings/unittests.py @@ -0,0 +1,59 @@ +import jsartoolkitNFT +import numpy as np +import unittest +import numpy.testing as npt + +class TestNFT(unittest.TestCase): + def setUp(self): + self.nft = jsartoolkitNFT.ARToolKitNFT() + self.cameraId = self.nft.loadCamera('../examples/Data/camera_para.dat') + self.id = self.nft.setup(640, 480, self.cameraId) + print('id:', self.id) + self.nearPlane = 0.1 + self.farPlane = 1000 + + def test_load_camera(self): + self.assertEqual(self.cameraId, 3) + + def test_setup(self): + self.assertEqual(self.id, 6) + + def test_setupAR2(self): + self.assertEqual(self.nft.setupAR2(), 0) + + def test_get_camera_lens(self): + expected_lens = np.array([ + 1.90724698e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 0.00000000e+00, 2.53244770e+00, 0.00000000e+00, 0.00000000e+00, + -1.23565148e-02, -7.90590035e-03, -1.00000020e+00, -1.00000000e+00, + 0.00000000e+00, 0.00000000e+00, -2.00000020e-04, 0.00000000e+00 + ]) + npt.assert_allclose(self.nft.getCameraLens(), expected_lens, rtol=1e-5, atol=1e-8) + + def test_set_projection_near_plane(self): + self.nft.setProjectionNearPlane(self.nearPlane) + self.assertEqual(self.nft.getProjectionNearPlane(), self.nearPlane) + + def test_set_projection_far_plane(self): + self.nft.setProjectionFarPlane(self.farPlane) + self.assertEqual(self.nft.getProjectionFarPlane(), self.farPlane) + + def test_add_nft_markers(self): + marker_ids = self.nft.addNFTMarkers(['../examples/DataNFT/pinball']) + print('marker_ids:', marker_ids) + self.assertEqual(marker_ids, [0]) + + def test_get_nft_data(self): + marker_ids = self.nft.addNFTMarkers(['../examples/DataNFT/pinball']) + print('marker_ids:', marker_ids) + if marker_ids: + nftData = self.nft.getNFTData(marker_ids[0]) + print('nftData:', nftData) + self.assertEqual(nftData.width_NFT, 893) + self.assertEqual(nftData.height_NFT, 1117) + self.assertEqual(nftData.dpi_NFT, 120) + else: + self.fail("No NFT markers were added.") + +if __name__ == '__main__': + unittest.main() \ No newline at end of file