Open anuj9196 opened 1 month ago
Hi @anuj9196 At the moment i have no idea what it could be the reason, at least i will try your dockerfile. Can you share the changes you made to src/NFTMarkerCreator.js
?
@kalwalt These are the changes
NFTMarkerCreator.js
from src
to root
(project directory), so that it can be directly accessed using node app.js
, without specifying the src/
path.app.js
to make it shorter to typesrcImage
and outputPath
to allow any path in flag -i
and -o
repsectively. The input and output directories can be any place if absolute path is passed.
node app.js -i /static/media/image.png -o /result/output
The app.js
file content is
const path = require("path");
const fs = require('fs');
const sharp = require('sharp');
const { prompt } = require('enquirer');
var Module = require('./build/NftMarkerCreator_wasm.js');
// GLOBAL VARs
var params = [
];
var validImageExt = [".jpg", ".jpeg", ".png"];
var srcImage;
var outputPath = 'output/';
var buffer;
var foundInputPath = {
b: false,
i: -1
}
var foundOutputPath = {
b: false,
i: -1
}
var noConf = false;
var withDemo = false;
var onlyConfidence = false;
var isZFT = false;
var imageData = {
sizeX: 0,
sizeY: 0,
nc: 0,
dpi: 0,
array: []
}
Module.onRuntimeInitialized = async function () {
console.log('arguments...: ', process.argv);
for (let j = 2; j < process.argv.length; j++) {
if (process.argv[j].indexOf('-i') !== -1 || process.argv[j].indexOf('-I') !== -1) {
foundInputPath.b = true;
foundInputPath.i = j + 1;
j++;
} else if (process.argv[j] === "-NoConf") {
noConf = true;
} else if (process.argv[j] === "-Demo") {
withDemo = true;
} else if (process.argv[j] === "-zft") {
isZFT = true;
} else if (process.argv[j] === "-onlyConfidence") {
onlyConfidence = true;
} else if (process.argv[j].indexOf('-o') !== -1 || process.argv[j].indexOf('-O') !== -1) {
foundOutputPath.b = true;
foundOutputPath.i = j + 1;
j++;
} else {
console.log(process.argv[j]);
params.push(process.argv[j]);
}
}
if (!foundInputPath.b) {
const response = await prompt({
type: 'input',
name: 'inputPath',
message: 'Image path not present, to continue provide a path to image:'
});
srcImage = response.inputPath;
} else {
srcImage = process.argv[foundInputPath.i];
}
if (!srcImage) {
console.log("\nERROR: No image in INPUT command!\n e.g:(-i /PATH/TO/IMAGE)\n");
process.exit(1);
} else {
console.log('checking file path...: ', srcImage);
if (!srcImage.startsWith('/')) {
// Relative path
srcImage = path.join(__dirname, srcImage);
}
console.log('image path is: ', srcImage);
// srcImage = path.join(__dirname, process.argv[foundInputPath.i]);
}
if (foundOutputPath.b) {
outputPath = process.argv[foundOutputPath.i];
if (!outputPath.startsWith('/')) {
// relative path
outputPath = path.join(__dirname, outputPath);
// outputPath = '/' + outputPath;
}
if (!outputPath.endsWith('/')) {
outputPath += '/';
}
console.log('Set output path: ' + outputPath);
}
let fileNameWithExt = path.basename(srcImage);
let fileName = path.parse(fileNameWithExt).name;
let extName = path.parse(fileNameWithExt).ext;
let foundExt = false;
for (let ext in validImageExt) {
if (extName.toLowerCase() === validImageExt[ext]) {
foundExt = true;
break;
}
}
if (!foundExt) {
console.log("\nERROR: Invalid image TYPE!\n Valid types:(jpg,JPG,jpeg,JPEG,png,PNG)\n");
process.exit(1);
}
if (!fs.existsSync(srcImage)) {
console.log("\nERROR: Not possible to read image, probably invalid image PATH!\n");
process.exit(1);
} else {
buffer = fs.readFileSync(srcImage);
}
console.log('Check output path: ' + outputPath);
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath);
}
if (extName.toLowerCase() === ".jpg" || extName.toLowerCase() === ".jpeg" || extName.toLowerCase() === ".png") {
await processImage(buffer);
}
let confidence = calculateQuality();
let txt = " - - - - - ";
if (confidence.l != 0) {
let str = txt.split(" ");
str.pop();
str.shift();
for (let i = 0; i < parseInt(confidence.l); i++) {
str[i] = " *";
}
str.push(" ");
txt = str.join("");
}
if (onlyConfidence) {
console.log("%f", confidence.l);
process.exit(0);
}
console.log("\nConfidence level: [" + txt + "] %f/5 || Entropy: %f || Current max: 5.17 min: 4.6\n", confidence.l, confidence.e)
if (noConf) {
await askToContinue();
}
let paramStr = params.join(' ');
let StrBuffer = Module._malloc(paramStr.length + 1);
Module.stringToUTF8(paramStr, StrBuffer);
console.log('Write Success');
let heapSpace = Module._malloc(imageData.array.length * imageData.array.BYTES_PER_ELEMENT);
Module.HEAPU8.set(imageData.array, heapSpace);
console.log('Setting Heap Success.. Continue to Create ImageSet..');
Module._createNftDataSet(heapSpace, imageData.dpi, imageData.sizeX, imageData.sizeY, imageData.nc, StrBuffer)
console.log('Create NFT Dataset complete...')
Module._free(heapSpace);
Module._free(StrBuffer);
let filenameIset = "tempFilename.iset";
let filenameFset = "tempFilename.fset";
let filenameFset3 = "tempFilename.fset3";
let ext = ".iset";
let ext2 = ".fset";
let ext3 = ".fset3";
let content = Module.FS.readFile(filenameIset);
let contentFset = Module.FS.readFile(filenameFset);
let contentFset3 = Module.FS.readFile(filenameFset3);
if (isZFT) {
console.log("CREATING ZFT FILE");
let iset = Buffer.from(content.buffer);
let fset = Buffer.from(contentFset.buffer);
let fset3 = Buffer.from(contentFset3.buffer);
let obj = {
iset: iset.toString('hex'),
fset: fset.toString('hex'),
fset3: fset3.toString('hex')
}
let strObj = JSON.stringify(obj);
let StrBufferZip = Module._malloc(strObj.length + 1);
Module.stringToUTF8(strObj, StrBufferZip);
Module._compressZip(StrBufferZip, strObj.length);
let contentBin = Module.FS.readFile("tempBinFile.bin");
// fs.writeFileSync(path.join(__dirname, '/output/') + fileName + ".zft", contentBin);
fs.writeFileSync(outputPath + fileName + ".zft", contentBin);
Module._free(StrBufferZip);
if (withDemo) {
console.log("\nFinished marker creation!\nNow configuring demo! \n")
const markerDir = path.join(__dirname, './../demo/public/marker/');
if (!fs.existsSync(markerDir)) {
fs.mkdirSync(markerDir);
}
let demoHTML = fs.readFileSync("./demo/nft.html").toString('utf8').split("\n");
addNewMarker(demoHTML, fileName);
let newHTML = demoHTML.join('\n');
fs.writeFileSync("./demo/nft.html", newHTML, { encoding: 'utf8', flag: 'w' });
const files = fs.readdirSync(markerDir);
for (const file of files) {
fs.unlink(path.join(markerDir, file), err => {
if (err) throw err;
});
}
fs.writeFileSync(markerDir + fileName + ".zft", contentBin);
console.log("Finished!\nTo run demo use: 'npm run demo'");
}
} else {
console.log("CREATING ISET, FSET AND FSET3 FILES");
// fs.writeFileSync(path.join(__dirname, outputPath) + fileName + ext, content);
// fs.writeFileSync(path.join(__dirname, outputPath) + fileName + ext2, contentFset);
// fs.writeFileSync(path.join(__dirname, outputPath) + fileName + ext3, contentFset3);
fs.writeFileSync(outputPath + fileName + ext, content);
fs.writeFileSync(outputPath + fileName + ext2, contentFset);
fs.writeFileSync(outputPath + fileName + ext3, contentFset3);
if (withDemo) {
console.log("\nFinished marker creation!\nNow configuring demo! \n")
const markerDir = path.join(__dirname, './../demo/public/marker/');
if (!fs.existsSync(markerDir)) {
fs.mkdirSync(markerDir);
}
let demoHTML = fs.readFileSync("./../demo/nft.html").toString('utf8').split("\n");
addNewMarker(demoHTML, fileName);
let newHTML = demoHTML.join('\n');
fs.writeFileSync("./../demo/nft.html", newHTML, { encoding: 'utf8', flag: 'w' });
const files = fs.readdirSync(markerDir);
for (const file of files) {
fs.unlink(path.join(markerDir, file), err => {
if (err) throw err;
});
}
fs.writeFileSync(markerDir + fileName + ext, content);
fs.writeFileSync(markerDir + fileName + ext2, contentFset);
fs.writeFileSync(markerDir + fileName + ext3, contentFset3);
console.log("Finished!\nTo run demo use: 'npm run demo'");
}
}
process.exit(0);
}
async function processImage(buf) {
const image = sharp(buf);
await image.metadata()
.then(async metadata => {
if (metadata.density) {
imageData.dpi = metadata.density;
} else {
console.log("\nWARNING: No DPI value found! Using 150 as default value!\n");
imageData.dpi = 150;
}
if(metadata.width){
imageData.sizeX = metadata.width;
} else {
await metadataWidth();
}
if(metadata.height){
imageData.sizeY = metadata.height;
} else {
await metadataHeigth();
}
if(metadata.channels){
imageData.nc = metadata.channels;
} else {
await metadataChannels();
}
return image
.raw()
.toBuffer()
})
.then(data => {
let dt = data.buffer
let verifyColorSpace = detectColorSpace(dt);
if (verifyColorSpace === 1) {
console.log("Color Space is 1 channel!");
} else if (verifyColorSpace === 3) {
console.log("Color Space is 3 channels!");
}
let uint = new Uint8Array(dt);
if(imageData.nc === verifyColorSpace){
console.log("Color Space is equal to metadata.channels!");
}else{
console.log("Color Space is not equal to metadata.channels!");
//process.exit(1);
}
imageData.nc = verifyColorSpace;
imageData.array = uint;
})
.catch(function (err) {
console.error("Error extracting metadata: " + err);
process.exit(1);
});
}
async function extractMetadata(buf) {
return await sharp(buf).metadata()
.then(function (metadata) {
if (metadata.density) {
imageData.dpi = metadata.density;
} else {
console.log("\nWARNING: No DPI value found! Using 150 as default value!\n");
imageData.dpi = 150;
}
imageData.sizeX = metadata.width;
imageData.sizeY = metadata.height;
imageData.nc = metadata.channels;
}).catch(function (err) {
console.error("Error extracting metadata: " + err);
process.exit(1);
});
}
function detectColorSpace(arr) {
let target = parseInt(arr.length / 4);
let counter = 0;
for (let j = 0; j < arr.length; j += 4) {
let r = arr[j];
let g = arr[j + 1];
let b = arr[j + 2];
if (r === g && r === b) {
counter++;
}
}
if (target === counter) {
return 1;
} else {
return 3;
}
}
function rgbaToRgb(arr) {
let newArr = [];
let BGColor = {
R: 255,
G: 255,
B: 255
}
for (let i = 0; i < arr.length; i += 4) {
let r = parseInt(255 * (((1 - arr[i + 3]) * BGColor.R) + (arr[i + 3] * arr[i])));
let g = parseInt(255 * (((1 - arr[i + 3]) * BGColor.G) + (arr[i + 3] * arr[i + 1])));
let b = parseInt(255 * (((1 - arr[i + 3]) * BGColor.B) + (arr[i + 3] * arr[i + 2])));
newArr.push(r);
newArr.push(g);
newArr.push(b);
}
return newArr;
}
function calculateQuality() {
let gray = toGrayscale(imageData.array);
let hist = getHistogram(gray);
let ent = 0;
let totSize = imageData.sizeX * imageData.sizeY;
for (let i = 0; i < 255; i++) {
if (hist[i] > 0) {
let temp = (hist[i] / totSize) * (Math.log(hist[i] / totSize));
ent += temp;
}
}
let entropy = (-1 * ent).toFixed(2);
let oldRange = (5.17 - 4.6);
let newRange = (5 - 0);
let level = (((entropy - 4.6) * newRange) / oldRange);
if (level > 5) {
level = 5;
} else if (level < 0) {
level = 0;
}
return { l: level.toFixed(2), e: entropy };
}
function toGrayscale(arr) {
let gray = [];
for (let i = 0; i < arr.length; i += 3) {
let avg = (arr[i] + arr[i + 1] + arr[i + 2]) / 3;
gray.push(parseInt(avg));
}
return gray;
}
function getHistogram(arr) {
let hist = [256];
for (let i = 0; i < arr.length; i++) {
hist[i] = 0;
}
for (let i = 0; i < arr.length; i++) {
hist[arr[i]]++;
}
return hist;
}
function addNewMarker(text, name) {
for (let i = 0; i < text.length; i++) {
if (text[i].trim().includes("<script>MARKER_NAME =")) {
text[i] = "<script>MARKER_NAME = '" + name + "'</script>"
break;
}
}
}
async function askToContinue() {
const response = await prompt({
type: 'input',
name: 'answer',
message: 'Do you want to continue? (Y/N)\n'
});
if (response.answer == "n") {
console.log("\nProcess finished by the user! \n");
process.exit(1);
}
}
async function metadataWidth() {
const responseToProceed = await prompt({
type: 'input',
name: 'answer',
message: 'Metadata width not present do you want to inform it? (Y/N)\n'
});
if (responseToProceed.answer == "n") {
console.log("\nProcess finished by the user! \n");
process.exit(1);
} else {
const responseAfterEnquiry = await prompt({
type: 'input',
name: 'width',
message: 'Inform the width: e.g 200\n'
});
if (responseAfterEnquiry.width) {
imageData.sizeX = responseAfterEnquiry.width;
}
}
}
async function metadataHeigth() {
const responseToProceed = await prompt({
type: 'input',
name: 'answer',
message: 'Metadata height not present do you want to inform it? (Y/N)\n'
});
if (responseToProceed.answer == "n") {
console.log("\nProcess finished by the user! \n");
process.exit(1);
} else {
const responseAfterEnquiry = await prompt({
type: 'input',
name: 'height',
message: 'Inform the height: e.g 400\n'
});
if (responseAfterEnquiry.height) {
imageData.sizeY = responseAfterEnquiry.height;
}
}
}
async function metadataChannels() {
const responseToProceed = await prompt({
type: 'input',
name: 'answer',
message: 'Metadata channels not present do you want to inform it? (Y/N)\n'
});
if (responseToProceed.answer == "n") {
console.log("\nProcess finished by the user! \n");
process.exit(1);
} else {
const responseAfterEnquiry = await prompt({
type: 'input',
name: 'channels',
message: 'Inform the number of channels: e.g 3\n'
});
if (responseAfterEnquiry.channels) {
imageData.nc = responseAfterEnquiry.channels;
}
}
}
NOTE: Path of demo
and build
are not updated.
NOTE: If you feel these changes are good to merged in this repo, I can create a PR for the same.
I think the issue is not with the library itself but with the dependencies required to run C++ executables. I tried using the python
base image instead of the slim
version.
FROM python:3.12
#FROM python:3.12-slim
And the command is executing. However, the stdout of the C++ command is not streaming live and it takes approx 15 minutes to complete the execution.
First I moved out the NFTMarkerCreator.js from src to root (project directory), so that it can be directly accessed using node app.js, without specifying the src/ path. I renamed the file to app.js to make it shorter to type
Maybe we can setup a script to add the NFTMarkerCreator.js to the system envirnonment vars
.
I will try as i have a bit of time.
And the command is executing. However, the stdout of the C++ command is not streaming live and it takes approx 15 minutes to complete the execution.
Probably it depends on dpi and width, height of the image you are providing for the NFT creations.
NOTE: If you feel these changes are good to merged in this repo, I can create a PR for the same.
I will think about, maybe i can create another repository and add the NFT-Marker-Creator-app as git submodule. I planned to do in the past but never had the time. @ThorstenBux in the past made an app to create NFT markers in a server, but i don't think it is working anymore. The repository is this https://github.com/webarkit/NFT-Creator-WS
I'm trying to build the image but it fails at the end because missed dev-requirements.txt
file, i think required for python. @anuj9196 Can you share also this? Thank you.
P.S. and maybe /scripts/docker/entrypoint.sh
?
@kalwalt Please check the content of requested files
dev-requirements.txt
django==5.0.6
djangorestframework==3.15.1
django-allauth==0.63.3
dj-rest-auth==6.0.0
django-oauth-toolkit==2.4.0
celery[sqs]==5.4.0
django-celery-beat==2.6.0
django-celery-results==2.5.1
django-filter==24.2
django-hosts==6.0
whitenoise==6.6.0
sentry-sdk==2.5.1
django-cors-headers==4.3.1
pre-commit==3.7.1
flake8==7.0.0
django-admin-search==0.3.15
boto3==1.34.125
django-storages==1.14.3
dj-database-url
psycopg==3.1.12
#django-postgres-extra==2.0.8 # No Django 5 support
#mysqlclient
django-safedelete==1.4.0
django-json-widget
jsonschema==4.23.0
jschon==0.11.0
django-ordered-model==3.7.4
django-silk
channels==4.1.0
channels_redis==4.2.0
daphne==4.1.2
uWSGI==2.0.26
pandas==2.2.2
openpyxl==3.1.5
django-import-export==4.0.8
django-libsass==0.9
opencv-python-headless==4.10.0.84
numpy==2.0.0
jet-django==1.9.11
dnspython==2.6.1
tld==0.13
django-countries==7.6.1
django-sequences==3.0
swapper==1.3.0
django-two-factor-auth==1.16.0
pyotp
geoip2==4.8.0
user-agents==2.2.0
css-inline==0.14.1
stripe
weasyprint==62.2
django-weasyprint==2.3.0
plotly==5.22.0
kaleido
markdown==3.6
newrelic
django-htmx
entrypoint.sh
#!/bin/sh
#set -e
# Accept other commands
exec "$@"
I was able to build the docker image with some modifications, creating a container and running a command to build a nft markers. But i avoided to use python as main command, instead i used the classical command node NFTMarkerCreator.js -i pinball.jpg
for testing. I haven't tested yet your modifications to the js script but i will do as a next step.
I had to do this because probably in your setup you have other scripts, and was a nonsense to me try with them. I would develop a basic docker image so anyone may fork the repository and modify in base of the needs. Probably will switch to a ubuntu base layer instead of python, infact i saw it enlarge a lot in size.
In regards of nfts creation times i don't see they are increased, probably it depends on dpi, image size.
@anuj9196 tested your mod to NFTMarkerCreator.js
, it works perfectly in my local docker image and
also when running the old style app (without docker i mean). Those changes are necessary for the docker image, many thanks for this! Soon i will upload on a branch this code, referencing this issue.
I have been trying to run the application to generate markers in a docker container running
Python
as base image andnode 18
as additional dependency. But it stucks onFull
Dockerfile
is likeThen running the node application using `
NOTE: I have made some changes to the
src/NFTMarkerCreator.js
file to allow absolute path to the file and output directory