I have a bunch of images, with different resolution.
Also there is a mix of landscape and portrait pictures. I need to resize the images to one resolution (1024x768). If i have a portrait picture, the max height needs to be 768, and my landscape pictures has to have a max width of 1024.
The space that is over, has to be made black.
Right now i use mogrify -resize 1024x768 -verbose *.jpg
I know i can use 1024x!768 , but like i said i'm using different kind of pictures.
My exif information also doesn't contains information about if a picture is landscape or not.
I use ImageMagick for such tasks. When installed, you have the "convert" command, which is very common, and does your task easyly.
You will have to crop the image to get the same aspect ratio, then you can resize the image to get the desired resolution. Example code using nodejs (imagemagick command line tools):
var width = 166;
var height = 117;
var ratio_new = width/height;
var ratio_old = image_file.width_orig/image_file.height_orig;
var pixels_too_much = 0;
var geometry = '';
if (ratio_old > ratio_new)
{
config.debug && console.log ("remove horizontal pixel!");
pixels_too_much = parseInt(image_file.width_orig - (image_file.height_orig * ratio_new))-1;
geometry = parseInt(image_file.height_orig * ratio_new + 0.5) + 'x' + image_file.height_orig;
geometry += "+" + parseInt(pixels_too_much/2) + "+0\!";
}
else if (ratio_old < ratio_new)
{
config.debug && console.log ("remove vertikal pixel");
pixels_too_much = parseInt(image_file.height_orig - (image_file.width_orig / ratio_new));
geometry = image_file.width_orig + 'x' + (image_file.width_orig / ratio_new);
geometry += "+0+" + parseInt(pixels_too_much/2)+"\!";
}
im.convert([image_file.path, '-crop', geometry, '-resize', width + 'x' + height, thumb_path],function(){});
Related
I need to resize images to fit in a specific dimensions. I want to keep aspect ratio.
For example
original image:
w:634
h:975
resize to max:
w:50
h:100
result:
w:50
h:85
I have not found anything that could do that(calculations for w and h)
and I am too dumb to figure it out by myself
copilot suggested me something that just keeps aspect ratio
If you want to use packages. I prefer jimp for image editing.
Jimp.read('image.jpg')
.then((lenna) => {
const isHorizontal = lenna.getWidth() > lenna.getHeight();
const ratio = isHorizontal
? lenna.getWidth() / lenna.getHeight()
: lenna.getHeight() / lenna.getWidth();
const width = 375; // set the width you want
const height = isHorizontal ? width / ratio : width * ratio;
return lenna.resize(width, height).quality(60).write("image.jpg");
})
.catch((err) => {
console.error(err);
});
Calculate the aspect ratio of your original image and of your maximum dimensions. According to that ratios, either take the maximum width or height as fixed and calculate the other side accordingly.
let
oheight = 634, owidth = 975,
mheight = 50, mwidth = 100,
theight, twidth;
let
oratio = owidth/oheight, // (~1.54)
mratio = mwidth/mheight, // (2)
if (mratio > oratio) {
//original image is "higher" so take the maximum height
//and calculate the width accordingly
theight = mheight; //50
twidth = theight * oratio; //77
} else {
//original image is "wider" so take the maximum width
//and calculate the height accordingly
twidth = mwidth;
theight = twidth / oratio;
}
But any decent image processing library will have the functionality, that you can pass in the maximum dimensions and define to keep the aspect ratio and will do these calculations internally ...
I am looking for the way of stretching a geometry (with all vertices z = 0) into visible screen (HTML Canvas Element).
For now I have worked out how to fit the geometry to the screen, like this:
with following code that basically adjusts camera.z to fit geometry to the height of canvas.
geometry.computeBoundingBox();
const bbox = geometry.boundingBox;
const geometryCenter = bbox.getCenter(new THREE.Vector3());
const geometrySize = bbox.getSize(new THREE.Vector3())
const cameraZ = getZFromGeometrySize(camera.fov, geometrySize);
const scale = getScaleFromZ(height, camera.fov, cameraZ);
const zoomTransform = d3.zoomIdentity
.translate(width * 0.5, height * 0.5)
.scale(scale);
zoom.transform(canvasSelection, zoomTransform);
camera.position.set(geometryCenter.x, geometryCenter.y, cameraZ)
camera.updateProjectionMatrix();
with below definitions of functions:
function getZFromGeometrySize(fov, geometrySize) {
const maxSize = Math.max( geometrySize.x, geometrySize.y );
const halfFOVRadians = toRadians(fov * 0.5);
return maxSize / ( 2 * Math.tan( halfFOVRadians ) );
}
function getScaleFromZ (height, fov, z) {
const halfFOVRadians = toRadians(fov * 0.5);
return height / (2 * Math.tan(halfFOVRadians) * z);
}
This however is using camera position so geometry will fit the view. However, I am looking for the way to stretch the geometry so its bounding box precisely fits the screen, ideally with some predefined padding.
Since this is not related to camera settings I need to manipulate geometry vertices values to stretch it horizontally. How to achieve this? I want to retain values of vertices as they relate to underlying data.
I assume this would need to be a function of canvas dimensions (width, height), geometry coordinates, and camera settings returning new geometry coordinates? Any hint is appreciated.
A short answer to this question is: to set camera's aspect ratio to 1.0.
This will work if geometry bounds are in clip space already [-1, 1 ]. If not they have to be converted to clip space first.
I am making a code to put a watermark on images. The only problem is that some images are in vertical mode and others are in horizontal mode. Due to this, vertically images are cropping in a square and adding a black background arround the square.
I have found nothing. I am using image.composite like this: https://github.com/oliver-moran/jimp/issues/175#issuecomment-255878441 but this does not resolve my problem. I resize the image that goes on the background image.
My actual code:
let jimp = require("Jimp")
let images = require("fs").readdirSync("./images")
images.forEach(image => {
let p1 = jimp.read("./images/"+image.split("/").pop());
Promise.all([p1, p2]).then(imgs => {
imgs[1].resize(imgs[0].getWidth() > imgs[0].getHeight() ? imgs[0].getWidth() / 50 * 4.166666666666667 * 1.75 : imgs[0].getHeight() / 50 * 4.166666666666667* 1.75,
imgs[0].getWidth() > imgs[0].getHeight() ? imgs[0].getWidth() / 50 * 4.166666666666667 * 1.75 : imgs[0].getHeight() / 50 * 4.166666666666667* 1.75,
() => {
imgs[0].composite(imgs[1], imgs[0].HORIZONTAL_ALIGN_RIGHT, imgs[0].VERTICAL_ALIGN_BOTTOM).write("./outputs/"+image.split("/").pop());
}
)
}).catch(console.error)
})
This works well on horizontal images but not on vertical.
Any help is appreciated.
Edit: I found that jimp read verticals images as horizontals. I don't know if canvas can do this better.
You can use the img.bitmap.width & img.bitmap.height property in JIMP to get the actual width and height of the image.
const width = item.bitmap.width
const height = item.bitmap.height
if (width > height)
// horizontal
else if (width == height)
// square
else
// vertical
Based on this you can proceed further on the logic
When you render text on an HTML5 canvas (using the fillText command, for example), the text will render anti-aliased, meaning the text looks smoother. The downside is that it becomes very noticable when trying to render small text or specifically non-aliased fonts (such as Terminal). Because of this, what I want to do is render text aliased, rather than anti-aliased.
Is there any way to do so?
Unfortunately, there is no native way to turn off anti-aliasing for text.
The solution is to use the old-school approach of bitmap fonts, that is, in the case of HTML5 canvas a sprite-sheet where you copy each bitmap letter to the canvas. By using a sprite-sheet with transparent background you can easily change the color/gradient etc. of it as well.
An example of such bitmap:
For it to work you need to know what characters it contains ("map"), the width and height of each character, and the width of the font bitmap.
Note: In most cases you'll probably end up with a mono-spaced font where all cells have the same size. You can use a proportional font but in that case you need to be aware of that you need to map each character with an absolute position and include the width and height as well for its cell.
An example with comments:
const ctx = c.getContext("2d"), font = new Image;
font.onload = () => {
// define some meta-data
const charWidth = 12; // character cell, in pixels
const charHeight = 16;
const sheetWidth = (font.width / charWidth)|0; // width, in characters, of the image itself
// map so we can use index of a char. to calc. position in bitmap
const charMap = " !\"#$% '()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ยง";
// Draw some demo text
const timeStart = performance.now();
fillBitmapText(font, "Demo text using bitmap font!", 20, 20);
fillBitmapText(font, "This is line 2...", 20, 45);
const timeEnd = performance.now();
console.log("Text above rendered in", timeEnd - timeStart, "ms");
// main example function
function fillBitmapText(font, text, x, y) {
// always make sure x and y are integer positions
x = x|0;
y = y|0;
// current x position
let cx = x;
// now, iterate over text per char.
for(let char of text) {
// get index in map:
const i = charMap.indexOf(char);
if (i >= 0) { // valid char
// Use index to calculate position in bitmap:
const bx = (i % sheetWidth) * charWidth;
const by = ((i / sheetWidth)|0) * charHeight;
// draw in character on canvas
ctx.drawImage(font,
// position and size from font bitmap
bx, by, charWidth, charHeight,
// position on canvas, same size
cx, y, charWidth, charHeight);
}
cx += charWidth; // increment current canvas x position
}
}
}
font.src = "//i.stack.imgur.com/GeawH.png";
body {background:#fff}
<canvas id=c width=640></canvas>
This should produce an output similar to this:
You can modify this to suit your needs. Notice that the bitmap used here is not transparent - I'll leave that to OP.
I'm trying to create loading bar for my game. I create basic rectangle and added to the stage and caluclated size acording to the number of files so I get fixed width. Everything works, but for every step (frame) it creates another rectangle, how do I get only one object?
this is my code:
function test(file) {
r_width = 500;
r_height = 20;
ratio = r_width / manifest.length;
if (file == 1) {
new_r_width = 0
// Draw
r = new createjs.Shape();
r_x = (width / 2) - (r_width / 2);
r_y = (height / 2) - (r_height / 2);
new_r_width += ratio;
r.graphics.beginFill("#222").drawRect(r_x, r_y, new_r_width, r_height);
stage.addChild(r);
} else {
stage.clear();
new_r_width += ratio;
r.graphics.beginFill("#" + file * 100).drawRect(r_x, r_y + file * 20, new_r_width, r_height);
stage.addChild(r);
}
stage.update();
}
https://space-clicker-c9-zoranf.c9.io/loading/
If you want to redraw the rectangle, you will have to clear the graphics first, and then ensure the stage is updated. In your code it looks like you are clearing the stage, which is automatically handled by the stage.update() unless you manually turn off updateOnTick.
There are some other approaches too. If you just use a rectangle, you can set the scaleX of the shape. Draw your rectangle at 100% of the size you want it at, and then scale it based on the progress (0-1).
r.scaleX = 0.5; // 50%
A new way that is supported (only in the NEXT version of EaselJS, newer than 0.7.1 in GitHub), you can save off the drawRect command, and modify it.
var r = new createjs.Shape();
r.graphics.beginFill("red");
var rectCommand = r.graphics.drawRect(0,0,100,10).command; // returns the command
// Later
rectCommand.w = 50; // Modify the width of the rectangle
Hope that helps!