I have an app, where I take a pic and draw it to the pdf file.
I have been trying to get them in right ratio. If I take all picture horizontal, it would be fine. If I take all pictures vertical, it would be fine. Problem is, when I take both vertical and horizontal, last picture's ratio applied to all pictures.
I save image ratio to AsyncStorage, where I get it in other component.
Questions is: How can I keep original ratios when I take pictures both ways?
Camera component -> takePicture function:
takePicture = async() => {
if (this.camera) {
const options = { quality: 0.5, base64: true , fixOrientation: true};
const data = await this.camera.takePictureAsync(options);
console.log(data.uri);
Image.getSize(data.uri, (width, height) => { // Get image height and width
let imageWidth = width;
let imageHeight = height;
let stringImageWidth = '' + imageWidth; // Convert to string for AsyncStorage saving
let stringImageHeight = '' + imageHeight;
const horizontalRatioCalc = () => { // return ratio for own variable
return (imageWidth/imageHeight);
};
const verticalRatioCalc = () => {
return (imageWidth/imageHeight);
};
horizontalImageRatio = horizontalRatioCalc();
verticalImageRatio = verticalRatioCalc();
stringHorizontalImageRatio = '' + horizontalImageRatio;
stringVerticalImageRatio = '' + verticalImageRatio;
console.log(`Size of the picture ${imageWidth}x${imageHeight}`);
horizontalRatio = async () => {
if (imageHeight>imageWidth) {
verticalRatioCalc();
try {
AsyncStorage.setItem("imageVerticalRatio", stringVerticalImageRatio),
AsyncStorage.setItem("asyncimageWidth", stringImageWidth),
AsyncStorage.setItem("asyncimageHeight", stringImageHeight),
console.log(`Vertical ratio saved! It's ${stringVerticalImageRatio}`),
console.log(`Image Width saved! It's ${stringImageWidth}`),
console.log(`Image height saved! It's ${stringImageHeight}`)
} catch (e) {
console.log(`AsyncStorage saving of image vertical ratio cannot be done.`)
}
}if (imageHeight<imageWidth) {
horizontalRatioCalc();
try {
AsyncStorage.setItem("imageHorizontalRatio", stringHorizontalImageRatio),
AsyncStorage.setItem("asyncimageWidth", stringImageWidth),
AsyncStorage.setItem("asyncimageHeight", stringImageHeight),
console.log(`Horizontal ratio saved! It's ${stringHorizontalImageRatio}`),
console.log(`Image Width saved! It's ${stringImageWidth}`),
console.log(`Image height saved! It's ${stringImageHeight}`)
} catch (e) {
console.log(`AsyncStorage saving of image vertical ratio cannot be done.`)
}
}
}
horizontalRatio();
}, (error) => {
console.error(`Cannot size of the image: ${error.message}`);
});
back(data.uri)
//CameraRoll.saveToCameraRoll(data.uri).then((data) => {
// console.log(data)
//back(data);
//}).catch((error) => {
//console.log(error)
//})
}
CreatePdf component -> PDF drawing function:
readData = async () => {
try {
kuvakorkeus = await AsyncStorage.getItem('asyncimageHeight')
kuvaleveys = await AsyncStorage.getItem('asyncimageWidth')
vertikaaliratio = await AsyncStorage.getItem('imageVerticalRatio')
horisontaaliratio = await AsyncStorage.getItem('imageHorizontalRatio')
if (kuvakorkeus !== null) {
return kuvakorkeus;
return kuvaleveys;
return vertikaaliratio;
return horisontaaliratio;
}
} catch (e) {
alert('Failed to fetch the data from storage')
}
}
readData ();
kuvanpiirto = () => {
pdfKuvaKorkeus = 100;
if(kuvakorkeus>kuvaleveys) { // VERTICAL / PYSTYKUVA
page.drawImage(arr[i].path.substring(7),'jpg',{
x: imgX,
y: imgY,
width: pdfKuvaKorkeus*vertikaaliratio,
height: pdfKuvaKorkeus,
})
}
if(kuvakorkeus<kuvaleveys) { // Horizontal / Vaakakuva
page.drawImage(arr[i].path.substring(7),'jpg',{
x: imgX,
y: imgY,
width: pdfKuvaKorkeus*horisontaaliratio,
height: pdfKuvaKorkeus,
})
}
}
kuvanpiirto();
Related
For a digital artwork I'm generating a canvas element in Vue which draws from an array of multiple images.
The images can be split in two categories:
SVG (comes with a fill-color)
PNG (just needs to be drawn as a regular image)
I came up with this:
const depict = (options) => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const myOptions = Object.assign({}, options);
if (myOptions.ext == "svg") {
return loadImage(myOptions.uri).then((img) => {
ctx.drawImage(img, 0, 0, 100, 100);
ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = myOptions.clr;
ctx.fillRect(0, 0, 100, 100);
ctx.globalCompositeOperation = "source-over";
});
} else {
return loadImage(myOptions.uri).then((img) => {
ctx.fillStyle = myOptions.clr;
ctx.drawImage(img, 0, 0, 100, 100);
});
}
};
this.inputs.forEach(depict);
for context:
myOptions.clr = the color
myOptions.uri = the url of the image
myOptions.ext = the extension of the image
While all images are drawn correctly I can't figure out why the last fillStyle overlays the whole image. I just want all the svg's to have the fillStyle which is attached to them.
I tried multiple globalCompositeOperation in different orders. I also tried drawing the svg between ctx.save and ctx.restore. No succes… I might be missing some logic here.
So! I figured it out myself in the meantime :)
I created an async loop with a promise. Inside this I created a temporary canvas per image which I then drew to one canvas. I took inspiration from this solution: https://stackoverflow.com/a/6687218/15289586
Here is the final code:
// create the parent canvas
let parentCanv = document.createElement("canvas");
const getContext = () => parentCanv.getContext("2d");
const parentCtx = getContext();
parentCanv.classList.add("grid");
// det the wrapper from the DOM
let wrapper = document.getElementById("wrapper");
// this function loops through the array
async function drawShapes(files) {
for (const file of files) {
await depict(file);
}
// if looped > append parent canvas to to wrapper
wrapper.appendChild(parentCanv);
}
// async image loading worked best
const loadImage = (url) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`load ${url} fail`));
img.src = url;
});
};
// depict the file
const depict = (options) => {
// make a promise
return new Promise((accept, reject) => {
const myOptions = Object.assign({}, options);
var childCanv = document.createElement("canvas");
const getContext = () => childCanv.getContext("2d");
const childCtx = getContext();
if (myOptions.ext == "svg") {
loadImage(myOptions.uri).then((img) => {
childCtx.drawImage(img, 0, 0, 100, parentCanv.height);
childCtx.globalCompositeOperation = "source-in";
childCtx.fillStyle = myOptions.clr;
childCtx.fillRect(0, 0, parentCanv.width, parentCanv.height);
parentCtx.drawImage(childCanv, 0, 0);
accept();
});
} else {
loadImage(myOptions.uri).then((img) => {
// ctx.fillStyle = myOptions.clr;
childCtx.drawImage(img, 0, 0, 100, parentCanv.height);
parentCtx.drawImage(childCanv, 0, 0);
accept();
});
}
});
};
drawShapes(this.inputs);
I am creating a handpose recognition app using mediapipe handpose. But the landmarks that I am getting from the estimatehands functions keeps on increasing and after certiain point i am receiving NAN of array as a output. The image that is received in the function is from tensor camera. Please do help. Thank You..
// run for every image in a video
let frame = 0;
const computeRecognitionEveryNFrames = 1;
const handleCameraStream = (images) => {
let outer = [];
let points = [];
const loop = async () => {
if (net) {
if (frame % computeRecognitionEveryNFrames === 0) {
const nextImageTensor = images.next().value;
if (nextImageTensor) {
if (outer.length > 90) outer = [];
if (points.length > 11340) points = [];
const objects = await net.estimateHands(nextImageTensor);
console.log(objects);
objects.length > 0
? objects.map((object) => {
object.landmarks.map((arr) => {
points.push(...arr);
});
})
: null;
// tf.dispose([nextImageTensor]);
nextImageTensor.dispose();
if (points.length >= 126) outer.push(points.slice(-126));
// if (outer.length >= 30) {
// (async () => {
// const model = await tf.loadLayersModel(
// bundleResourceIO(modelJSON, modelWeights)
// );
// // console.log(outer);
// const ypreds = Array.from(
// model
// .predict(tf.tensor(outer.slice(-30)).expandDims(0))
// .dataSync()
// );
// // console.log(ypreds.indexOf(Math.max(ypreds)));
// console.log(ypreds);
// })();
// }
console.log(outer.length);
}
}
frame += 1;
frame = frame % computeRecognitionEveryNFrames;
}
requestAnimationFrame(loop);
};
loop();
};
=========================================================
<TensorCamera
style={{
width: 256,
height: height * 0.8,
marginRight: "auto",
marginLeft: "auto",
}}
resizeHeight={height * 0.8}
resizeDepth={3}
resizeWidth={256}
type={Camera.Constants.Type.front}
autorender={true}
onReady={(images) => handleCameraStream(images)}
/>
</View>```
[![landmarks][1]][1]
[1]: https://i.stack.imgur.com/pR0JW.png
I am new with React Native, so be patient my friends. I have been struggling with getting the image size outside of the Image.getSize function.
First problem: Console is logging first console.log('Image.getSize funktion ulkopuolella koko on ${imgWidth} x ${imgHeight}') while there is Image.getSize before that. Tried re-order them with no luck. And it's logging image size 0x0.
I want use those width and height for drawing image. But I do not know how to "get them out" from the Image.getSize function. How I can do it?
The point in this function is to get image size for calculating right image ratio for resizing.
kuvanpiirto = () => {
pdfKuvaKorkeus = 100;
let imgWidth = 0;
let imgHeight = 0;
Image.getSize(arr[i].path, (width, height) => {
console.log(`Kuvan leveys PDF luonnissa on ${width}`) // logittaa kuvan leveyden
console.log(`Kuvan korkeus PDF luonnissa on ${height}`) // logittaa kuvan korkeuden
imgWidth = width;
imgHeight = height;
});
console.log(`Image.getSize funktion ulkopuolella koko on ${imgWidth} x ${imgHeight}`) // logittaa kuvan leveyden
if(imgHeight>imgWidth) { // VERTICAL / PYSTYKUVA
page.drawImage(arr[i].path.substring(7),'jpg',{
x: imgX,
y: imgY,
width: pdfKuvaKorkeus*1.33,
height: pdfKuvaKorkeus,
})
}
if(imgHeight<imgWidth) { // Horizontal / Vaakakuva
page.drawImage(arr[i].path.substring(7),'jpg',{
x: imgX,
y: imgY,
width: pdfKuvaKorkeus*0.75,
height: pdfKuvaKorkeus,
})
}
}
kuvanpiirto();
Tried also put all code inside Image.getSize function like this:
kuvanpiirto = () => {
Image.getSize(arr[i].path, (width, height) => {
imgWidth = width;
imgHeight = height;
if(imgHeight>imgWidth) { // VERTICAL / PYSTYKUVA
console.log(`Vertical - IMAGE SIZE ${imgWidth}X${imgHeight}`)
console.log(`IMAGE PATH ${arr[i].path}`)
page.drawImage(arr[i].path.substring(7),'jpg',{
x: imgX,
y: imgY,
width: 100*1.33,
height: 100,
})
}
if(imgHeight<imgWidth) { // Horizontal / Vaakakuva
console.log(`Horizontal - IMAGE SIZE ${imgWidth}X${imgHeight}`)
console.log(`IMAGE PATH ${arr[i].path}`)
page.drawImage(arr[i].path.substring(7),'jpg',{
x: imgX,
y: imgY,
width: 100*0.75,
height: 100,
})
}
});
}
kuvanpiirto();
It works better, but not draw an image. Giving error, picture below. Do not have any glue why it cannot draw the image.
I didn't check but it should work
kuvanpiirto = async () => {
// your code
const {imgWidth, imgHeight} = await new Promise((resolve) => {
Image.getSize(img, (width, height) => {
resolve({imgWidth: width, imgHeight: height});
});
});
// your code
};
useEffect ( ()=>{
Image.getSize(uri ,(width,height)=>{
setWidth(width)
setHeight(height)
}
},[uri])
I have an app, where i need to get known ratio of picture for resizing it later. User can take pictures horizontal and vertical. And of course, ratio changes. So i need to get two different ratios.
There is problem with updating let horizontalImageRatio = 0;
How can i solve this, any tips?
my code:
Image.getSize(data.uri, (width, height) => { // Pickuping size of the picture
let imageWidth = width;
let imageHeight = height;
let stringImageWidth = '' + imageWidth; // Make double to string for storing to asyncstorage
let stringImageHeight = '' + imageHeight;
let horizontalImageRatio = 0; // this value should be updated
let verticalImageRatio = 0;
// For updating let horizontalImageRatio, but not working outside of this <-- PROBLEM
const horizontalRatioCalc = () => {
horizontalImageRatio = imageWidth/imageHeight;
console.log('horizontal_update', horizontalImageRatio);
};
const verticalRatioCalc = () => {
verticalImageRatio = imageWidth/imageHeight;
console.log('vertical_update', verticalImageRatio);
};
let stringHorizontalImageRatio = '' + horizontalImageRatio;
let stringVerticalImageRatio = '' + verticalImageRatio;
console.log(`Size of the picture ${imageWidth}x${imageHeight}`); // Tells size of the image for the console
horizontalRatio = async () => {
if (imageHeight>imageWidth) {
verticalRatioCalc();
try {
AsyncStorage.setItem("imageVerticalRatio", stringVerticalImageRatio),
AsyncStorage.setItem("asyncimageWidth", stringImageWidth)
AsyncStorage.setItem("asyncimageHeight", stringImageHeight)
console.log(`Vertical ratio saved! It's ${stringVerticalImageRatio}`)
console.log(`Image Width saved! It's ${stringImageWidth}`)
console.log(`Image height saved! It's ${stringImageHeight}`)
} catch (e) {
console.log(`AsyncStorage saving of image vertical ratio cannot be done.`)
}
}else {
horizontalRatioCalc();
try {
AsyncStorage.setItem("imageHorizontalRatio", stringHorizontalImageRatio),
AsyncStorage.setItem("asyncimageWidth", stringImageWidth)
AsyncStorage.setItem("asyncimageHeight", stringImageHeight)
console.log(`Horizontal ratio saved! It's ${stringHorizontalImageRatio}`)
console.log(`Image Width saved! It's ${stringImageWidth}`)
console.log(`Image height saved! It's ${stringImageHeight}`)
} catch (e) {
console.log(`AsyncStorage saving of image vertical ratio cannot be done.`)
}
}
}
horizontalRatio();
So, apparently, you don't have access to horizontalImageRatio inside of horizontalRatioCalc, the method horizontalRatioCalc has a different scope.
What you can do is change horizontalRatioCalc to receive a parameter and return a value, just like I've done in this example: https://stackblitz.com/edit/react-xiysai
This is good because now you have a function that can be tested independently.
Or you can also do it this way:
https://stackblitz.com/edit/react-variable-scope
This way you have access to both the variable and the method.
I'm working on some code to calculate numColumns for a flatlist- intention is 3 on landscape tablet, 2 on portrait tablet, and 1 on portrait phone.
Here's my code:
const [width, setWidth] = useState(Dimensions.get('window').width);
const [imageWidth, setImageWidth] = useState(100);
const [imageHeight, setImageHeight] = useState(100);
const [columns, setColumns] = useState(3);
useEffect(() => {
function handleChange() {
setWidth(Dimensions.get('window').width);
}
Dimensions.addEventListener("change", handleChange);
return () => Dimensions.removeEventListener("change", handleChange);
}, [width]);
useEffect(() => {
if (width > 1100) {
setColumns(3);
} else if (width <= 1099 && width > 600) {
setColumns(2);
} else {
setColumns(1);
}
setImageWidth((width - (64 * columns) + 15) / columns);
setImageHeight(((width - (64 * columns) + 15) / columns) * .6);
}, [width]);
imageWidth and imageHeight are passed to the render component of the flatlist to size an image.
It seems to work fine when I load it in landscape mode, but if I rotate to portrait, I get this:
Then, if I go back to landscape, it stays as 2 columns?
Any idea how I can fix this?
You're not returning the height of the device, afterwards, you need to calculate the orientation of the device (isLandscape).
Logically it flows as the following:
is the device landscape? (setColumns 3)
is the device wide and portrait? (set columns 2)
others (setColumns 1)
From there you can pass that into the second useEffect, (say, useColumnsHook). This should be able to set the height/width based on orientation of the device.
I also recommend setting the height/width based on percentages rather than exact pixels for devices (100%, 50%, 33.3%).
const [width, setWidth] = useState(Dimensions.get('window').width);
const [imageWidth, setImageWidth] = useState(100);
const [imageHeight, setImageHeight] = useState(100);
const [columns, setColumns] = useState(3);
/**
* orientation
*
* return {
* width,
* height
* }
*/
const useScreenData = () => {
const [screenData, setScreenData] = useState(Dimensions.get("screen"))
useEffect(() => {
const onChange = (result) => {
setScreenData(result.screen)
}
Dimensions.addEventListener("change", onChange)
return () => Dimensions.removeEventListener("change", onChange)
})
return {
...screenData,
}
}
const { width, height } = useScreenData()
const isLandscape = width > height
useEffect(() => {
if (isLandscape && width > 1100) {
// handle landscape
setColumns(3)
} else if (!isLandscape && (width <= 1099 && width > 600)) {
setColumns(2)
} else {
setColumns(1)
}
setImageWidth((width - (64 * columns) + 15) / columns);
setImageHeight(((width - (64 * columns) + 15) / columns) * .6);
}, [width, isLandscape]);
1:Tablet or Phone
You can use the react-native-device-info package along with the Dimensions API. Check the isTablet() method and apply different styles according on the result.
if you care Expo user then you have to user expo-constants userInterfaceIdiom method to detect isTablet Reference
import DeviceInfo from 'react-native-device-info';
let isTablet = DeviceInfo.isTablet();
2:Portrait mode or Landscape mode
then you can find portrait mode or landscape mode by
const isPortrait = () => {
const dim = Dimensions.get('screen');
return dim.height >= dim.width;
};
3:Code:
import React, { useState, useEffect } from "react";
import { View, Text, Dimensions } from "react-native";
import DeviceInfo from "react-native-device-info";
const App = () => {
const isLandscapeFunction = () => {
const dim = Dimensions.get("screen");
return dim.width >= dim.height;
};
const [width, setWidth] = useState(Dimensions.get("window").width);
const [imageWidth, setImageWidth] = useState(100);
const [imageHeight, setImageHeight] = useState(100);
const [columns, setColumns] = useState(3);
let isTabletPhone = DeviceInfo.isTablet();;
useEffect(() => {
function handleChange(result) {
setWidth(result.screen.width);
const isLandscape = isLandscapeFunction();
if (isLandscape && isTabletPhone) {
setColumns(3);
} else if (!isLandscape && isTabletPhone) {
setColumns(2);
} else {
setColumns(1);
}
}
Dimensions.addEventListener("change", handleChange);
return () => Dimensions.removeEventListener("change", handleChange);
});
useEffect(() => {
console.log(columns);
setImageWidth((width - 64 * columns + 15) / columns);
setImageHeight(((width - 64 * columns + 15) / columns) * 0.6);
}, [columns, width]);
return (
<View style={{ justifyContent: "center", alignItems: "center", flex: 1 }}>
<Text>imageWidth {imageWidth}</Text>
<Text>imageHeight {imageHeight}</Text>
</View>
);
};
export default App;