puffinsoft / jscanify

Open-source Javascript mobile document scanner.
https://puffinsoft.github.io/jscanify/
MIT License
956 stars 52 forks source link

Size Ratio for exported canvas. #24

Closed thasneem077 closed 1 month ago

thasneem077 commented 9 months ago

how to set auto height or width to maintain the ratio of the image size instead of fixed width and height? scanner.extractPaper(image, paperWidth, paperHeight)

TomAtRichfords commented 8 months ago

This is how I did it (and it seems to be working ok). In jscanify.js...

Change this: extractPaper(image, resultWidth, resultHeight, cornerPoints) {

To this: extractPaper(image, maxSize, cornerPoints) {

Then after this: let warpedDst = new cv.Mat();

Insert this:

 // Calculate "width" and "height" of the quadrilateral
    const widthTop = Math.sqrt(Math.pow(topRightCorner.x - topLeftCorner.x, 2) + Math.pow(topRightCorner.y - topLeftCorner.y, 2));
    const widthBottom = Math.sqrt(Math.pow(bottomRightCorner.x - bottomLeftCorner.x, 2) + Math.pow(bottomRightCorner.y - bottomLeftCorner.y, 2));
    const heightLeft = Math.sqrt(Math.pow(topLeftCorner.x - bottomLeftCorner.x, 2) + Math.pow(topLeftCorner.y - bottomLeftCorner.y, 2));
    const heightRight = Math.sqrt(Math.pow(topRightCorner.x - bottomRightCorner.x, 2) + Math.pow(topRightCorner.y - bottomRightCorner.y, 2));

    // Average the widths and heights to account for potential skew
    const avgWidth = (widthTop + widthBottom) / 2;
    const avgHeight = (heightLeft + heightRight) / 2;

    // Determine aspect ratio
    const aspectRatio = avgWidth / avgHeight;

    // Calculate output dimensions based on aspect ratio and max size
    let resultWidth, resultHeight;
    if (aspectRatio > 1) { // Landscape
      resultWidth = maxSize;
      resultHeight = Math.round(maxSize / aspectRatio);
    } else { // Portrait or square
      resultWidth = Math.round(maxSize * aspectRatio);
      resultHeight = maxSize;
    }

Before this: let dsize = new cv.Size(resultWidth, resultHeight);

You would then use scanner.extractPaper(image, maxSize), where maxSize is the largest side of your desired scanned image. ps. ChatGPT generated it 🤣

ColonelParrot commented 8 months ago

Yep, this solution looks like it might work too. If you want to avoid unnecessary complexity though, you could simply show your users a dialog, asking them "what paper size would you like the scan to be?"

thenewguy commented 7 months ago

This would be very useful when scanning a variety of papers with unknown size - such as notes a customer gives you as a stack of random paper

thenewguy commented 7 months ago

Really it would be most useful to me if the image was extracted and returned at its real dimensions. It can be scaled to a different size later as long as the result could be queried for true dimensions.

thenewguy commented 7 months ago

Adding how you can achieve it since I ended up here in my initial search.

Here is how you determine capture the width & height to use for your ratio:

const img = cv.imread(imageEl);

const contour = scanner.findPaperContour(img);
console.log(contour);

const cornerPoints = scanner.getCornerPoints(contour);
console.log(cornerPoints);

const topWidth = cornerPoints.topRightCorner.x - cornerPoints.topLeftCorner.x;
const bottomWidth = cornerPoints.bottomRightCorner.x - cornerPoints.bottomLeftCorner.x;
const leftHeight = cornerPoints.bottomLeftCorner.y - cornerPoints.topLeftCorner.y;
const rightHeight = cornerPoints.bottomRightCorner.y - cornerPoints.topRightCorner.y;

console.log(topWidth);
console.log(bottomWidth);
console.log(leftHeight);
console.log(rightHeight);

const width = Math.max(topWidth, bottomWidth);
const height = Math.max(leftHeight, rightHeight);

console.log(width);
console.log(height);

const resultCanvas = scanner.extractPaper(imageEl, width, height);

The final width and height is at the ratio to scale

ColonelParrot commented 1 month ago

This feature is not easy to implement, and IMO is out of the scope of this library. If you'd like, here's an answer on StackOverflow with a solution using Python. You can get the corner points with the getCornerPoints method and go from there.