Илья Бизунов 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 个回答 Voted Илья Бизунов 2020-08-26T13:19:17Z2020-08-26T13:19:17Z 我还没有找到一个通用的解决方案,但我现在将描述它是如何工作的,也许它会对某人有所帮助。 现在,根据图片的数量,我将高度(350 像素)除以第 N 行数。最大 - 三,因为。只能有10张图片。 接下来,我填写这样的行: 我计算每一行的总纵横比 在符合这个值最低的我加一张图片 如果还有图片 - 第 1 点。 现在我设置图片的宽度(高度,记住,它只是 350 / 行数)。我这样做: 选择下一行 我得到另一张照片 图像宽度 = 510 * (Picture_Aspect Ratio / Total_Picture_Time_Aspect Ratio) 等等。 那么,根据这个算法工作的代码: '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 显示相同图片的方式: 不难注意到,在这种排列中,显示了第一张图片的更大部分(可能是所有其他图片)。 正因为如此,我认为我的算法“不通用”。 Best Answer sercxjo 2020-09-04T21:29:30Z2020-09-04T21:29:30Z 保存图片序列的算法。 我们将行数定义为 sqrt(所有图像的纵横比之和/块的纵横比),其中纵横比定义为宽度除以高度。 我们将行高定义为块高度除以行数。 确定缩放到行高的所有图像的总长度。 我们填充线条,使线条宽度尽可能接近剩余图像的宽度除以剩余线条数。 重新分配行高。为此,对于每一行,我们计算新的高度,使行宽等于块宽。分别针对溢出行和底部填充行总结了新旧行高之间的差异。根据哪个差异较大,我们按比例减少其中一组的高度变化,使这些数量相等。(示例中的复选标记取消了最后的对齐方式,它允许您保持图片的纵横比,但违反了块的高度)。 对于剩余可用空间(直到块的宽度)的行,我们缩放图像以占据块的整个宽度,之后我们在高度上裁剪它们并将它们垂直居中。 在没有可用空间的行中,我们缩放图像,使它们的高度等于行高,并根据它们的纵横比按水平居中裁剪宽度,使总行宽等于块宽度. 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> 改变图片顺序的算法。 我们将行数定义为 sqrt(所有图像的纵横比之和/块的纵横比),其中纵横比定义为宽度除以高度。 我们将行高定义为块高度除以行数。 按纵横比对图像进行排序,将最宽的放在最前面。 我们将图像按行分布,将下一张图像放在具有最大可用空间的行中,其定义为块的宽度减去缩放后图像的宽度之和,使其高度等于行高。 对于剩余可用空间(直到块的宽度)的行,我们缩放图像以占据块的整个宽度,之后我们在高度上裁剪它们并将它们垂直居中。 在没有可用空间的行中,我们缩放图像,使它们的高度等于行高,并根据它们的纵横比按水平居中裁剪宽度,使总行宽等于块宽度. 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> Aim X 2020-08-31T04:58:03Z2020-08-31T04:58:03Z 看看这些解决方案的方向,我不确定这是否正是您所需要的,但我认为它可以帮助解决您的问题。 行网格.js 另一个脚本,但有一些描述(ENG) Qwertiy 2020-09-04T19:05:08Z2020-09-04T19:05:08Z 除了第一行之外的所有行都具有相同的高度。高度本身并不重要,比例很重要,所以一开始你可以随便拿。对于这个高度,迭代行数和其中的图片数。将剩余的图片发送到第一行。选择排列最均匀的选项作为结果。
我还没有找到一个通用的解决方案,但我现在将描述它是如何工作的,也许它会对某人有所帮助。
现在,根据图片的数量,我将高度(350 像素)除以第 N 行数。最大 - 三,因为。只能有10张图片。
接下来,我填写这样的行:
现在我设置图片的宽度(高度,记住,它只是 350 / 行数)。我这样做:
那么,根据这个算法工作的代码:
更新 09/01/2016 16:50
应要求,我将解释我的理解“不是通用解决方案”。我的算法不知道如何尽可能有效地分割图片。
这是使用我的算法显示三幅图像的方式: 请注意,它们都具有相同的宽度。第一张图(红色)被剪掉了非常非常多,因为 它具有所有图片中最大的宽度。
这就是 VK 显示相同图片的方式: 不难注意到,在这种排列中,显示了第一张图片的更大部分(可能是所有其他图片)。
正因为如此,我认为我的算法“不通用”。
保存图片序列的算法。
我们将行数定义为 sqrt(所有图像的纵横比之和/块的纵横比),其中纵横比定义为宽度除以高度。
我们将行高定义为块高度除以行数。
确定缩放到行高的所有图像的总长度。
我们填充线条,使线条宽度尽可能接近剩余图像的宽度除以剩余线条数。
重新分配行高。为此,对于每一行,我们计算新的高度,使行宽等于块宽。分别针对溢出行和底部填充行总结了新旧行高之间的差异。根据哪个差异较大,我们按比例减少其中一组的高度变化,使这些数量相等。(示例中的复选标记取消了最后的对齐方式,它允许您保持图片的纵横比,但违反了块的高度)。
对于剩余可用空间(直到块的宽度)的行,我们缩放图像以占据块的整个宽度,之后我们在高度上裁剪它们并将它们垂直居中。
在没有可用空间的行中,我们缩放图像,使它们的高度等于行高,并根据它们的纵横比按水平居中裁剪宽度,使总行宽等于块宽度.
改变图片顺序的算法。
我们将行数定义为 sqrt(所有图像的纵横比之和/块的纵横比),其中纵横比定义为宽度除以高度。
我们将行高定义为块高度除以行数。
按纵横比对图像进行排序,将最宽的放在最前面。
我们将图像按行分布,将下一张图像放在具有最大可用空间的行中,其定义为块的宽度减去缩放后图像的宽度之和,使其高度等于行高。
对于剩余可用空间(直到块的宽度)的行,我们缩放图像以占据块的整个宽度,之后我们在高度上裁剪它们并将它们垂直居中。
在没有可用空间的行中,我们缩放图像,使它们的高度等于行高,并根据它们的纵横比按水平居中裁剪宽度,使总行宽等于块宽度.
看看这些解决方案的方向,我不确定这是否正是您所需要的,但我认为它可以帮助解决您的问题。
行网格.js
另一个脚本,但有一些描述(ENG)
除了第一行之外的所有行都具有相同的高度。高度本身并不重要,比例很重要,所以一开始你可以随便拿。对于这个高度,迭代行数和其中的图片数。将剩余的图片发送到第一行。选择排列最均匀的选项作为结果。