RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 557940
Accepted
Илья Бизунов
Илья Бизунов
Asked:2020-08-22 14:47:25 +0000 UTC2020-08-22 14:47:25 +0000 UTC 2020-08-22 14:47:25 +0000 UTC

使用什么算法将多张照片显示为马赛克?

  • 772

比如在VK上传照片的时候。这是关于这样的显示:

在此处输入图像描述

UPD: 也许TreeMap 算法用于显示图像。但不清楚如何为图像分配“权重”。

алгоритм
  • 4 4 个回答
  • 10 Views

4 个回答

  • Voted
  1. Илья Бизунов
    2020-08-26T13:19:17Z2020-08-26T13:19:17Z

    我还没有找到一个通用的解决方案,但我现在将描述它是如何工作的,也许它会对某人有所帮助。

    现在,根据图片的数量,我将高度(350 像素)除以第 N 行数。最大 - 三,因为。只能有10张图片。

    接下来,我填写这样的行:

    1. 我计算每一行的总纵横比
    2. 在符合这个值最低的我加一张图片
    3. 如果还有图片 - 第 1 点。

    现在我设置图片的宽度(高度,记住,它只是 350 / 行数)。我这样做:

    1. 选择下一行
    2. 我得到另一张照片
    3. 图像宽度 = 510 * (Picture_Aspect Ratio / Total_Picture_Time_Aspect Ratio)
    4. 等等。

    那么,根据这个算法工作的代码:

    'use strict';
    
    angular.module('imageGrid').controller('imageGridController', function($scope) {
    
        var divWidth = 510;
        var divHeight = 350;
        var padding = 5;
    
        var imagesList = $scope.photos;
        angular.forEach(imagesList, function(image, index) {
            image.aspectRatio = image.width / image.height;
            image.blockWidth = 0;
            image.blockHeight = 0;
            image.marginBottom = 0;
            image.marginRight = 0;
        }, this);
    
        var sumOfWidth = function(set) {
            var totalWidth = 0; 
            if (set.length > 0) {
                for (var i = 0; i < set.length; i++) {
                    totalWidth += set[i].width;
                }
            }
            return totalWidth;
        }
    
        var sumOfAspectRatio = function(set) {
            var totalAspectRatio = 0; 
            if (set.length > 0) {
                for (var i = 0; i < set.length; i++) {
                    totalAspectRatio += set[i].aspectRatio;
                }
            }
            return totalAspectRatio;
        }
    
        var medianAspectRatio = function(set) {
            return this.sumOfAspectRatio(set);
        }
    
        var linearPartition = function(set) {
            var rows = Math.min(3, Math.ceil(set.length / 3));                
    
            var rowsArray = [];
            for (var i = 0; i < rows; i++) {
                rowsArray.push([]);
            }
    
            for (var l = 0; l < set.length; l++) {
                var rowsAspectRatio = [];
                for (var i = 0; i < rowsArray.length; i++) {
                    rowsAspectRatio.push(sumOfAspectRatio(rowsArray[i]));
                }
                var minIndex = 0, minValue = Infinity;
                for (var i = 0; i < rowsArray.length; i++) {
                    if (rowsAspectRatio[i] < minValue) {
                        minValue = rowsAspectRatio[i];
                        minIndex = i;
                    }
                }
                rowsArray[minIndex].push(set[l]);
            }
    
            return rowsArray;
        }
    
        var setByRows = linearPartition(imagesList);
        var rowsCount = setByRows.length;
    
        var virtualHeight = Math.floor(divHeight / rowsCount);
    
        for (var i = 0; i < setByRows.length; i++) {
            var rowAspectRatio = sumOfAspectRatio(setByRows[i]);
            var imagesCount = setByRows[i].length;
            var virtualWidth = imagesCount > 1 ? divWidth - (imagesCount - 1) * padding : divWidth;
            for (var l = 0; l < imagesCount; l++) {
                var image = setByRows[i][l];
                image.blockWidth = Math.floor(virtualWidth / imagesCount);
                image.blockHeight = virtualHeight;
            }
    
        }
    
        var resultList = [];
        for (var i = 0; i < rowsCount; i++) {
            var imagesCount = setByRows[i].length;
            for (var l = 0; l < setByRows[i].length; l++) {
                var image = setByRows[i][l];
                image.marginBottom = rowsCount > 1 && i < rowsCount - 1 ? padding : 0;
                image.marginRight = imagesCount > 1 && l < imagesCount - 1 ? padding : 0;
                resultList.push(image);
            }
        }
    
        $scope.imageList = resultList;
    });
    

    更新 09/01/2016 16:50

    应要求,我将解释我的理解“不是通用解决方案”。我的算法不知道如何尽可能有效地分割图片。

    这是使用我的算法显示三幅图像的方式: 请注意,它们都具有相同的宽度。第一张图(红色)被剪掉了非常非常多,因为 它具有所有图片中最大的宽度。在此处输入图像描述

    这就是 VK 显示相同图片的方式: 不难注意到,在这种排列中,显示了第一张图片的更大部分(可能是所有其他图片)。在此处输入图像描述

    正因为如此,我认为我的算法“不通用”。

    • 11
  2. Best Answer
    sercxjo
    2020-09-04T21:29:30Z2020-09-04T21:29:30Z

    保存图片序列的算法。

    1. 我们将行数定义为 sqrt(所有图像的纵横比之和/块的纵横比),其中纵横比定义为宽度除以高度。

    2. 我们将行高定义为块高度除以行数。

    3. 确定缩放到行高的所有图像的总长度。

    4. 我们填充线条,使线条宽度尽可能接近剩余图像的宽度除以剩余线条数。

    5. 重新分配行高。为此,对于每一行,我们计算新的高度,使行宽等于块宽。分别针对溢出行和底部填充行总结了新旧行高之间的差异。根据哪个差异较大,我们按比例减少其中一组的高度变化,使这些数量相等。(示例中的复选标记取消了最后的对齐方式,它允许您保持图片的纵横比,但违反了块的高度)。

    6. 对于剩余可用空间(直到块的宽度)的行,我们缩放图像以占据块的整个宽度,之后我们在高度上裁剪它们并将它们垂直居中。

    7. 在没有可用空间的行中,我们缩放图像,使它们的高度等于行高,并根据它们的纵横比按水平居中裁剪宽度,使总行宽等于块宽度.

    var iimgs=document.getElementById("iimgs").childNodes;
    var c=document.getElementById("c");
    var images=[];
    function packimgs(freeH) {
      var padding = 5;
      var divWidth = 510+padding;
      var divHeight = 350+padding;
    
      var w=0;
      for(var i=0;i<images.length;i++) {
        images[i].aspect=images[i].width/images[i].height;
        w+= images[i].aspect;
      }
      var nrows= Math.round(Math.sqrt(w*divHeight/divWidth));
      // удаление лишних строк от слишком широких картинок
      for(var i=0;i<images.length;i++)
        if(images[i].aspect > 1.5*w/nrows) nrows-= Math.round(images[i].aspect*nrows/w)-1;
      if(nrows<1) nrows=1;
      var rows= [];
      var rowHeight= divHeight/nrows - padding;
      w= rowHeight*w + padding*images.length;
      for(var j=0; j<nrows; j++) {
        rows[j]=[];
        rows[j].space= divWidth;
      }
      j=0;
      var rowWidth=0;
      for(i=0; i<images.length; i++) {
        var imgWidth= rowHeight*images[i].aspect+padding;
        if(j<nrows-1 && rows[j].length>0 && Math.abs(rowWidth-w/(nrows-1-j)) < Math.abs(rowWidth-w/(nrows-1-j)+imgWidth*2)) {
          rowWidth=0; j++;
        }
        rows[j].push(images[i]);
        rows[j].space-= imgWidth;
        rowWidth+= imgWidth;
        w-= imgWidth;
      }
      // Подгон высот
      var dhp=0, dhm=0, pn=0;
      for(j=0; j<nrows; j++) {
        var rowWidth1= divWidth-padding*rows[j].length
        var rowWidth= rowWidth1-rows[j].space;
        rows[j].height= rowHeight*rowWidth1/rowWidth;
        if(rows[j].height>rowHeight) {
          dhp+= rows[j].height-rowHeight;
          pn++;
        } else dhm+= rowHeight-rows[j].height;
      }
      // второй этап подгона высот (пропустить если не нужно точно соблюдать высоту блока)
      if(!freeH&&dhp!=dhm) for(j=0; j<nrows; j++) {
        if(rows[j].height>rowHeight && dhp>dhm)
          rows[j].height= rowHeight + (rows[j].height-rowHeight)*dhm/dhp;
        else if(rows[j].height<rowHeight && dhp<dhm)
          rows[j].height= rowHeight + (rows[j].height-rowHeight)*dhp/dhm;
      }
      // Заполнение блока
      for(j=0; j<nrows; j++) {
        var row=document.createElement("DIV");
        row.className='row';
        c.appendChild(row);
        var rowWidth1= divWidth-padding*rows[j].length
        var rowWidth= rowWidth1-rows[j].space;
        var rowHeight1= rows[j].space>0? rowHeight*rowWidth1/rowWidth : rows[j].height;
        for(i=0;i<rows[j].length;i++) {
          var cell=document.createElement("DIV");
          cell.className='cell';
          cell.appendChild(rows[j][i]);
          row.appendChild(cell);
          rows[j][i].style.height= rowHeight1+"px";
          if(rows[j].space>0) {
            cell.style.height= rows[j].height+"px";
            rows[j][i].style.marginTop= (rows[j].height-rowHeight1)/2+"px";
          } else {
            var cwidth= rows[j][i].aspect*rowWidth1*rowHeight/rowWidth;
            cell.style.width= cwidth+"px";
            rows[j][i].style.marginLeft= (cwidth-rows[j][i].width)/2+"px";
          }
        }
      }
    }
    function chlimit(i) {
      c.innerHTML="";
      packimgs(i.checked);
    }
    images.loaded=0;
    for(i=0;i<iimgs.length;i++) {
      var img= iimgs[i];
      images.push(img);
      img.onload=function() {
        if(images.length==++images.loaded) packimgs();
      }
    }
    .hidden{display:none;}
    div{margin:0; padding:0; line-height:0;}
    .cell{display:inline-block; margin-right:5px; margin-bottom:5px; overflow:hidden;}
    <div class=hidden id=iimgs><img src="http://goo.gl/jD9FJR"><img src="http://goo.gl/A7mxvM"><img src="http://goo.gl/H0K4y0"><img src="http://goo.gl/a8bSmt"><img src="http://goo.gl/oQ3X02"><img src="http://goo.gl/BfO6qj"><img src="http://goo.gl/E6XhJs"><img src="http://goo.gl/adrfhX"><img src="http://goo.gl/OkKx3A"></div><label><input type=checkbox onclick=chlimit(this)>Свободная высота (без обрезки)</label>
    <div id="c"></div>

    改变图片顺序的算法。

    1. 我们将行数定义为 sqrt(所有图像的纵横比之和/块的纵横比),其中纵横比定义为宽度除以高度。

    2. 我们将行高定义为块高度除以行数。

    3. 按纵横比对图像进行排序,将最宽的放在最前面。

    4. 我们将图像按行分布,将下一张图像放在具有最大可用空间的行中,其定义为块的宽度减去缩放后图像的宽度之和,使其高度等于行高。

    5. 对于剩余可用空间(直到块的宽度)的行,我们缩放图像以占据块的整个宽度,之后我们在高度上裁剪它们并将它们垂直居中。

    6. 在没有可用空间的行中,我们缩放图像,使它们的高度等于行高,并根据它们的纵横比按水平居中裁剪宽度,使总行宽等于块宽度.

    var iimgs=document.getElementById("iimgs").childNodes;
    var c=document.getElementById("c");
    var images=[];
    function packimgs() {
      var divWidth = 510;
      var divHeight = 350;
      var padding = 5;
    
      var w=0;
      for(var i=0;i<images.length;i++) {
        images[i].aspect=images[i].width/images[i].height;
        w+= images[i].aspect;
      }
      images.sort(function(a,b){return b.aspect-a.aspect;});
      var nrows= Math.round(Math.sqrt(w*divHeight/divWidth));
      // удаление лишних строк от слишком широких картинок
      for(var i=0;i<images.length;i++)
        if(images[i].aspect > 1.5*w/nrows) nrows-= Math.round(images[i].aspect*nrows/w)-1;
      if(nrows<1) nrows=1;
      var rows= [];
      var rowHeight= (divHeight-padding*(nrows-1))/nrows;
      for(var j=0; j<nrows; j++) {
        rows[j]=[]
        rows[j].space= divWidth;
      }
      for(i=0; i<images.length; i++) {
        var maxSp=rows[0].space, maxj=0;
        for(var j=1; j<nrows; j++) if(maxSp<rows[j].space) {
          maxSp=rows[j].space;
          maxj=j;
        }
        rows[maxj].space-= rowHeight*images[i].aspect;
        rows[maxj].push(images[i]);
        if(rows[maxj].length>1) rows[maxj].space-= padding;
      }
      for(j=0; j<nrows; j++) {
        var row=document.createElement("DIV");
        row.className='row';
        c.appendChild(row);
        var rowWidth1= divWidth-padding*(rows[j].length-1)
        var rowWidth= rowWidth1-rows[j].space;
        var rowHeight1= rows[j].space>0? rowHeight*rowWidth1/rowWidth : rowHeight;
        for(i=0;i<rows[j].length;i++) {
          var cell=document.createElement("DIV");
          cell.className='cell';
          cell.appendChild(rows[j][i]);
          row.appendChild(cell);
          rows[j][i].height= rowHeight1;
          if(rows[j].space>0) {
            cell.style.height= rowHeight+"px";
            rows[j][i].style.marginTop= (rowHeight-rowHeight1)/2+"px";
          } else {
            var cwidth= rows[j][i].width*rowWidth1/rowWidth;
            cell.style.width= cwidth+"px";
            rows[j][i].style.marginLeft= (cwidth-rows[j][i].width)/2+"px";
          }
        }
      }
    }
    for(i=0;i<iimgs.length;i++) {
      var img= iimgs[i];
      img.onload=function() {
        images.push(this);
        if(images.length==iimgs.length) packimgs();
      }
    }
    .hidden{display:none;}
    div{margin:0; padding:0; line-height:0;}
    .cell{display:inline-block; margin-right:5px; margin-bottom:5px; overflow:hidden;}
    <div class=hidden id=iimgs><img src="http://goo.gl/jD9FJR"><img src="http://goo.gl/A7mxvM"><img src="http://goo.gl/H0K4y0"><img src="http://goo.gl/a8bSmt"><img src="http://goo.gl/oQ3X02"><img src="http://goo.gl/BfO6qj"><img src="http://goo.gl/E6XhJs"><img src="http://goo.gl/adrfhX"><img src="http://goo.gl/OkKx3A"></div>
    <div id="c"></div>

    • 8
  3. Aim X
    2020-08-31T04:58:03Z2020-08-31T04:58:03Z

    看看这些解决方案的方向,我不确定这是否正是您所需要的,但我认为它可以帮助解决您的问题。

    行网格.js

    另一个脚本,但有一些描述(ENG)

    • 4
  4. Qwertiy
    2020-09-04T19:05:08Z2020-09-04T19:05:08Z

    除了第一行之外的所有行都具有相同的高度。高度本身并不重要,比例很重要,所以一开始你可以随便拿。对于这个高度,迭代行数和其中的图片数。将剩余的图片发送到第一行。选择排列最均匀的选项作为结果。

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5