在画布上最糟糕的质量透视图像

时间:2015-02-23 22:09:25

标签: javascript html5 image image-processing canvas

我的项目有问题。

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

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

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

原始图像尺寸:6500 x 3592,你可以看到图像上的扩散边缘......

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

我可以做些什么来解决这个问题?你怎么看待这个?

修改

更多细节;

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

感谢。

2 个答案:

答案 0 :(得分:1)

解决。

我在画布上使用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();
};

})();

答案 1 :(得分:0)

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

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

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

Here is one way 逐步降级图像。