p5.js中如何通过冒泡排序将排序后像素恢复为原始图像?
问题根因
你当前的反向逻辑无法还原原始图像的核心原因是:像素完成正向排序后,原始像素的位置信息已经完全丢失,单纯反转冒泡排序的比较逻辑和遍历顺序,只会将图像从色相升序排列改为色相降序排列,不可能回到初始的原始图像状态。你最初设想的存储原始像素位置/原始像素数据的思路才是正确的。
你的操作合理性判断
- 你认为正确的两个操作:修改比较运算符、两个分支都调用
loadPixels()和updatePixels(),逻辑是正确的,只是只能实现正反向的色相排序,无法还原原图。 - 你不确定的两个操作:反转for循环遍历顺序、修改相邻像素索引为
(j, i - 1),符合反向冒泡排序的实现逻辑,本身没有问题。 - 你提到的循环触发问题,是因为你在else分支中没有更新
counter,也没有加还原完成的判断逻辑,所以程序会永远卡在反向排序分支中。
实现方案
- 加载原图后立即存储一份原始像素数据,这份数据全程不修改,作为还原的基准。
- 新增状态变量标记当前执行阶段:正向排序、反向还原、重置待重新排序。
- 反向还原阶段以原始像素的色相为基准做冒泡交换,还原完成后直接重置为原始像素保证100%恢复。
- 新增阶段切换逻辑,还原完成后自动重置状态触发下一轮正向排序。
修改后代码
let panel; let originalPixels = []; let counter = 0; const sc = 2; const frameW = 20; // 状态标记:0=正向排序 1=反向还原 let state = 0; // 正向排序迭代阈值 const SORT_THRESHOLD = 3200000; function preload() { panel = loadImage("https://www.paulwheeler.us/files/Burano-island-Venice.jpg"); } function setup() { createCanvas( sc * (panel.width + 2 * frameW), sc * (panel.height + 2 * frameW) ); pixelDensity(1); // 加载完成后存储原始像素 panel.loadPixels(); originalPixels = [...panel.pixels]; } function draw() { panel.loadPixels(); if (state === 0) { // 正向排序阶段 for (let i = 0; i < panel.height - 1; i++) { for (let j = 0; j < panel.width; j++) { counter++; if (hue(fGetPanelPixel(j, i)) > hue(fGetPanelPixel(j, i + 1))) { let hold = fGetPanelPixel(j, i + 1); fSetPanelPixel(j, i + 1, fGetPanelPixel(j, i)); fSetPanelPixel(j, i, hold); } } } // 达到阈值切换到还原阶段 if (counter >= SORT_THRESHOLD) { state = 1; counter = 0; } } else { // 反向还原阶段:以原始像素为基准交换 let swapCount = 0; for (let i = panel.height - 1; i > 0; i--) { for (let j = 0; j < panel.width; j++) { const currentHue = hue(fGetPanelPixel(j, i)); const targetHue = hue(fGetOriginalPixel(j, i)); const prevHue = hue(fGetPanelPixel(j, i - 1)); const prevTargetHue = hue(fGetOriginalPixel(j, i - 1)); // 如果当前位置像素的色相和原始目标差距比上一个大,就交换 if (Math.abs(currentHue - targetHue) < Math.abs(prevHue - prevTargetHue)) { let hold = fGetPanelPixel(j, i - 1); fSetPanelPixel(j, i - 1, fGetPanelPixel(j, i)); fSetPanelPixel(j, i, hold); swapCount++; } } } counter++; // 连续多帧没有交换说明还原完成,重置为初始状态,切换回正向排序 if (swapCount === 0 || counter > SORT_THRESHOLD) { // 直接重置为原始像素保证完全还原 panel.pixels = [...originalPixels]; state = 0; counter = 0; } } panel.updatePixels(); image(panel, sc * frameW, sc * frameW, sc * panel.width, sc * panel.height); } function fGetPanelPixel(x, y) { const index = 4 * (y * panel.width + x); return [ panel.pixels[index], panel.pixels[index + 1], panel.pixels[index + 2], ]; } function fSetPanelPixel(x, y, c) { const index = 4 * (y * panel.width + x); panel.pixels[index] = c[0]; panel.pixels[index + 1] = c[1]; panel.pixels[index + 2] = c[2]; } // 读取原始像素的工具函数 function fGetOriginalPixel(x, y) { const index = 4 * (y * panel.width + x); return [ originalPixels[index], originalPixels[index + 1], originalPixels[index + 2], ]; }
配套HTML代码保持不变:
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
内容的提问来源于stack exchange,提问作者idma
相关产品推荐
相关产品推荐

