【问题标题】:Worst quality perspective image on canvas画布上质量最差的透视图
【发布时间】:2015-04-25 09:45:39
【问题描述】:

我的项目有问题。

我正在为设计师开发一个透视模型创建模块。用户上传图像,我将它们放置在模型中并进行一些透视计算。然后用户可以下载此图像。我用js在客户端完成了所有这些。

但是对于在画布上绘制的图像,使用这样的透视计算存在问题;

示例图片:http://oi62.tinypic.com/2h49dec.jpg

原始图像尺寸:6500 x 3592,您可以在图像上看到展开的边缘...

我尝试了一些技术,如 ctx.imageSmoothingEnabled true 等。但结果总是一样的。

我能做些什么来解决这个问题?您对此有何看法?

编辑

更多详情;

我从用户那里得到一张图片(无分辨率),然后将其裁剪为模型比例。例如,在我的示例图像中,用户图像被裁剪为 imac 比例 16:9,然后使用四点屏幕进行计算。顺便说一句,我的模型图像尺寸是 6500 x 3592。所以我对这个裁剪的图像进行了缩放、变换等操作,并将其放在画布上的模型中。然后使用 blob 将此图像下载到客户端...

谢谢。

【问题讨论】:

  • 如果放大图像,经常会出现像素化现象。输入像素密度更大的图像,放大通常不会显示那么多像素化。

标签: javascript html image image-processing canvas


【解决方案1】:

问题是(很可能,但没有代码显示如此......)图像实际上太大了。

画布通常使用双线性插值(2x2 样本)而不是双三次(4x4 样本)。这意味着,如果您在一个块中将其缩小很大比例,算法将跳过一些原本应该被采样的像素,从而产生更多像素化的外观。

解决方案是逐步调整图像大小,即。自身可重复 50%,直到达到合适的尺寸。然后对其使用透视计算。确切的目标大小需要通过反复试验来找到,但一个好的起点是使用生成的透视图的最大边。

Here is one way 逐步缩小图像的大小。

【讨论】:

  • 坦率地说,我很困惑 :-z 我查看了@OgzhnOzlci 的图像,看到了试图放大图像的一部分(在缩放框中),我认为问题是关于放大。但是,如果问题是缩小 6500 x 3592 图像,那么您就有了一个很好的答案和一个很好的“字里行间阅读”问题。 :-)
  • @markE 我认为输入图像是原始图像,但您可能是对的,它可能是示例图像的原始大小并且它没有说明输出大小。 OP可能应该澄清一下。
【解决方案2】:

解决了。

我使用perspective.js 在画布上进行计算。所以我对这个js源码做了一些修改。

如果您想使用或查看来源;

// Copyright 2010 futomi  http://www.html5.jp/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0

// perspective.js v0.0.2
// 2010-08-28
/* -------------------------------------------------------------------
 * define objects (name space) for this library.
 * ----------------------------------------------------------------- */
if (typeof html5jp == 'undefined') {
    html5jp = new Object();
}

(function() {


html5jp.perspective = function(ctxd, image) {
    // check the arguments
    if (!ctxd || !ctxd.strokeStyle) {
        return;
    }
    if (!image || !image.width || !image.height) {
        return;
    }
    // prepare a <canvas> for the image
    var cvso = document.createElement('canvas');
    cvso.width = parseInt(image.width) * 2;
    cvso.height = parseInt(image.height) * 2;
    var ctxo = cvso.getContext('2d');
    ctxo.drawImage(image, 0, 0, cvso.width, cvso.height);
    // prepare a <canvas> for the transformed image
    var cvst = document.createElement('canvas');
    cvst.width = ctxd.canvas.width;
    cvst.height = ctxd.canvas.height;
    var ctxt = cvst.getContext('2d');

    ctxt.imageSmoothingEnabled = true;
    ctxt.mozImageSmoothingEnabled = true;
    ctxt.webkitImageSmoothingEnabled = true;
    ctxt.msImageSmoothingEnabled = true;


    // parameters
    this.p = {
        ctxd: ctxd,
        cvso: cvso,
        ctxo: ctxo,
        ctxt: ctxt
    }
};

var proto = html5jp.perspective.prototype;

proto.draw = function(points) {
    var d0x = points[0][0];
    var d0y = points[0][1];
    var d1x = points[1][0];
    var d1y = points[1][1];
    var d2x = points[2][0];
    var d2y = points[2][1];
    var d3x = points[3][0];
    var d3y = points[3][1];
    // compute the dimension of each side
    var dims = [
        Math.sqrt(Math.pow(d0x - d1x, 2) + Math.pow(d0y - d1y, 2)), // top side
        Math.sqrt(Math.pow(d1x - d2x, 2) + Math.pow(d1y - d2y, 2)), // right side
        Math.sqrt(Math.pow(d2x - d3x, 2) + Math.pow(d2y - d3y, 2)), // bottom side
        Math.sqrt(Math.pow(d3x - d0x, 2) + Math.pow(d3y - d0y, 2)) // left side
    ];
    //
    var ow = this.p.cvso.width;
    var oh = this.p.cvso.height;
    // specify the index of which dimension is longest
    var base_index = 0;
    var max_scale_rate = 0;
    var zero_num = 0;
    for (var i = 0; i < 4; i++) {
        var rate = 0;
        if (i % 2) {
            rate = dims[i] / ow;
        } else {
            rate = dims[i] / oh;
        }
        if (rate > max_scale_rate) {
            base_index = i;
            max_scale_rate = rate;
        }
        if (dims[i] == 0) {
            zero_num++;
        }
    }
    if (zero_num > 1) {
        return;
    }
    //
    var step = 0.10;
    var cover_step = step * 250;
    //
    var ctxo = this.p.ctxo;
    var ctxt = this.p.ctxt;
    //*** ctxt.clearRect(0, 0, ctxt.canvas.width, ctxt.canvas.height);
    if (base_index % 2 == 0) { // top or bottom side
        var ctxl = this.create_canvas_context(ow, cover_step);
        var cvsl = ctxl.canvas;
        for (var y = 0; y < oh; y += step) {
            var r = y / oh;
            var sx = d0x + (d3x - d0x) * r;
            var sy = d0y + (d3y - d0y) * r;
            var ex = d1x + (d2x - d1x) * r;
            var ey = d1y + (d2y - d1y) * r;
            var ag = Math.atan((ey - sy) / (ex - sx));
            var sc = Math.sqrt(Math.pow(ex - sx, 2) + Math.pow(ey - sy, 2)) / ow;
            ctxl.setTransform(1, 0, 0, 1, 0, -y);
            ctxl.drawImage(ctxo.canvas, 0, 0);
            //
            ctxt.translate(sx, sy);
            ctxt.rotate(ag);
            ctxt.scale(sc, sc);
            ctxt.drawImage(cvsl, 0, 0);
            //
            ctxt.setTransform(1, 0, 0, 1, 0, 0);
        }
    } else if (base_index % 2 == 1) { // right or left side
        var ctxl = this.create_canvas_context(cover_step, oh);
        var cvsl = ctxl.canvas;
        for (var x = 0; x < ow; x += step) {
            var r = x / ow;
            var sx = d0x + (d1x - d0x) * r;
            var sy = d0y + (d1y - d0y) * r;
            var ex = d3x + (d2x - d3x) * r;
            var ey = d3y + (d2y - d3y) * r;
            var ag = Math.atan((sx - ex) / (ey - sy));
            var sc = Math.sqrt(Math.pow(ex - sx, 2) + Math.pow(ey - sy, 2)) / oh;
            ctxl.setTransform(1, 0, 0, 1, -x, 0);
            ctxl.drawImage(ctxo.canvas, 0, 0);
            //
            ctxt.translate(sx, sy);
            ctxt.rotate(ag);
            ctxt.scale(sc, sc);
            ctxt.drawImage(cvsl, 0, 0);
            //
            ctxt.setTransform(1, 0, 0, 1, 0, 0);
        }
    }
    // set a clipping path and draw the transformed image on the destination canvas.
    this.p.ctxd.save();
    this.set_clipping_path(this.p.ctxd, [
        [d0x, d0y],
        [d1x, d1y],
        [d2x, d2y],
        [d3x, d3y]
    ]);
    this.p.ctxd.drawImage(ctxt.canvas, 0, 0);
    this.p.ctxd.restore();
}



proto.create_canvas_context = function(w, h) {
    var canvas = document.createElement('canvas');
    canvas.width = w;
    canvas.height = h;
    var ctx = canvas.getContext('2d');

    ctx.imageSmoothingEnabled = true;
    ctx.mozImageSmoothingEnabled = true;
    ctx.webkitImageSmoothingEnabled = true;
    ctx.msImageSmoothingEnabled = true;


    return ctx;
};

proto.set_clipping_path = function(ctx, points) {
    ctx.beginPath();
    ctx.moveTo(points[0][0], points[0][1]);
    for (var i = 1; i < points.length; i++) {
        ctx.lineTo(points[i][0], points[i][1]);
    }
    ctx.closePath();
    ctx.clip();
};

})();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-14
    • 2012-12-05
    • 2013-11-11
    • 1970-01-01
    • 1970-01-01
    • 2012-10-08
    • 2014-04-12
    • 2014-05-03
    相关资源
    最近更新 更多