(imageData, strength)
| 683 | } |
| 684 | |
| 685 | applyPrefilter(imageData, strength) { |
| 686 | // 简单的双边滤波去除噪点 |
| 687 | const { data, width, height } = imageData; |
| 688 | const filtered = new Uint8ClampedArray(data); |
| 689 | |
| 690 | const kernelRadius = 1; |
| 691 | const spatialSigma = 1.0; |
| 692 | const colorSigma = 30.0 * strength; |
| 693 | |
| 694 | for (let y = kernelRadius; y < height - kernelRadius; y++) { |
| 695 | for (let x = kernelRadius; x < width - kernelRadius; x++) { |
| 696 | const centerIndex = (y * width + x) * 4; |
| 697 | const centerR = data[centerIndex]; |
| 698 | const centerG = data[centerIndex + 1]; |
| 699 | const centerB = data[centerIndex + 2]; |
| 700 | |
| 701 | let sumR = 0, sumG = 0, sumB = 0, sumWeight = 0; |
| 702 | |
| 703 | for (let dy = -kernelRadius; dy <= kernelRadius; dy++) { |
| 704 | for (let dx = -kernelRadius; dx <= kernelRadius; dx++) { |
| 705 | const neighborIndex = ((y + dy) * width + (x + dx)) * 4; |
| 706 | const neighborR = data[neighborIndex]; |
| 707 | const neighborG = data[neighborIndex + 1]; |
| 708 | const neighborB = data[neighborIndex + 2]; |
| 709 | |
| 710 | const spatialWeight = Math.exp(-(dx * dx + dy * dy) / (2 * spatialSigma * spatialSigma)); |
| 711 | |
| 712 | const colorDistance = Math.sqrt( |
| 713 | (centerR - neighborR) ** 2 + |
| 714 | (centerG - neighborG) ** 2 + |
| 715 | (centerB - neighborB) ** 2 |
| 716 | ); |
| 717 | const colorWeight = Math.exp(-(colorDistance * colorDistance) / (2 * colorSigma * colorSigma)); |
| 718 | |
| 719 | const weight = spatialWeight * colorWeight; |
| 720 | |
| 721 | sumR += neighborR * weight; |
| 722 | sumG += neighborG * weight; |
| 723 | sumB += neighborB * weight; |
| 724 | sumWeight += weight; |
| 725 | } |
| 726 | } |
| 727 | |
| 728 | filtered[centerIndex] = Math.round(sumR / sumWeight); |
| 729 | filtered[centerIndex + 1] = Math.round(sumG / sumWeight); |
| 730 | filtered[centerIndex + 2] = Math.round(sumB / sumWeight); |
| 731 | } |
| 732 | } |
| 733 | |
| 734 | data.set(filtered); |
| 735 | } |
| 736 | |
| 737 | async quantizeColors(imageData, colorCount, enableDithering) { |
| 738 | // 使用现有的median cut算法 |
no outgoing calls
no test coverage detected