画像フィルター完全ガイド|プロ級エフェクトとアート効果の実装技術
画像フィルターの仕組み、Instagram風フィルター、アート効果、カラーグレーディング、リアルタイム処理、GPUアクセラレーション、カスタムフィルター作成まで、4500字で徹底解説
画像フィルター完全ガイド
はじめに:画像フィルターの芸術と科学
画像フィルターは、写真を芸術作品に変換する強力なツールです。SNSの普及により、誰もがプロフェッショナルな画像編集を行える時代になりました。本記事では、画像フィルターの技術的な仕組みから、Instagram風エフェクト、アート効果、GPUを活用した高速処理まで、実装技術を包括的に解説します。
💡 統計データ: Instagram 2024レポートによると、投稿画像の89%にフィルターが適用され、フィルター使用画像は平均38%多いエンゲージメントを獲得しています。
第1章:画像フィルターの基礎理論
1.1 カラー変換の数学
RGB色空間での変換行列
class ColorTransform {
// カラーマトリックス変換
applyColorMatrix(pixels, matrix) {
const data = pixels.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
data[i] = r * matrix[0] + g * matrix[1] + b * matrix[2] + matrix[3] * 255;
data[i + 1] = r * matrix[4] + g * matrix[5] + b * matrix[6] + matrix[7] * 255;
data[i + 2] = r * matrix[8] + g * matrix[9] + b * matrix[10] + matrix[11] * 255;
// クランプ処理
data[i] = Math.max(0, Math.min(255, data[i]));
data[i + 1] = Math.max(0, Math.min(255, data[i + 1]));
data[i + 2] = Math.max(0, Math.min(255, data[i + 2]));
}
return pixels;
}
// 基本フィルター行列
getFilterMatrices() {
return {
grayscale: [
0.299, 0.587, 0.114, 0,
0.299, 0.587, 0.114, 0,
0.299, 0.587, 0.114, 0,
0, 0, 0, 1
],
sepia: [
0.393, 0.769, 0.189, 0,
0.349, 0.686, 0.168, 0,
0.272, 0.534, 0.131, 0,
0, 0, 0, 1
],
vintage: [
0.6, 0.3, 0.1, 0.03,
0.2, 0.7, 0.1, 0.02,
0.2, 0.3, 0.5, 0.03,
0, 0, 0, 1
],
polaroid: [
1.438, -0.062, -0.062, 0,
-0.122, 1.378, -0.122, 0,
-0.016, -0.016, 1.483, 0,
0, 0, 0, 1
]
};
}
}
1.2 畳み込みフィルター
エッジ検出とぼかし効果
class ConvolutionFilter {
applyKernel(imageData, kernel, kernelSize) {
const pixels = imageData.data;
const width = imageData.width;
const height = imageData.height;
const output = new Uint8ClampedArray(pixels);
const half = Math.floor(kernelSize / 2);
for (let y = half; y < height - half; y++) {
for (let x = half; x < width - half; x++) {
let r = 0, g = 0, b = 0;
for (let ky = 0; ky < kernelSize; ky++) {
for (let kx = 0; kx < kernelSize; kx++) {
const px = x + kx - half;
const py = y + ky - half;
const idx = (py * width + px) * 4;
const weight = kernel[ky * kernelSize + kx];
r += pixels[idx] * weight;
g += pixels[idx + 1] * weight;
b += pixels[idx + 2] * weight;
}
}
const outputIdx = (y * width + x) * 4;
output[outputIdx] = r;
output[outputIdx + 1] = g;
output[outputIdx + 2] = b;
}
}
imageData.data.set(output);
return imageData;
}
getKernels() {
return {
blur: [
1/9, 1/9, 1/9,
1/9, 1/9, 1/9,
1/9, 1/9, 1/9
],
gaussianBlur: [
1/16, 2/16, 1/16,
2/16, 4/16, 2/16,
1/16, 2/16, 1/16
],
sharpen: [
0, -1, 0,
-1, 5, -1,
0, -1, 0
],
edgeDetect: [
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
],
emboss: [
-2, -1, 0,
-1, 1, 1,
0, 1, 2
]
};
}
}
第2章:Instagram風フィルターの実装
2.1 人気フィルターの再現
Instagram風エフェクト
class InstagramFilters {
// Clarendon フィルター
clarendon(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// コントラスト増加
this.adjustContrast(imageData, 1.2);
// 彩度増加
this.adjustSaturation(imageData, 1.35);
// シアンのオーバーレイ
this.applyOverlay(imageData, { r: 127, g: 187, b: 227 }, 0.2);
ctx.putImageData(imageData, 0, 0);
}
// Nashville フィルター
nashville(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 暖色系シフト
this.applyColorShift(imageData, {
red: 20,
green: 10,
blue: -30
});
// コントラスト調整
this.adjustContrast(imageData, 1.2);
// ピンクのオーバーレイ
this.applyOverlay(imageData, { r: 247, g: 218, b: 174 }, 0.3);
// ヴィンテージ効果
this.addVignette(canvas);
ctx.putImageData(imageData, 0, 0);
}
// Valencia フィルター
valencia(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// 暖色トーン
const warmMatrix = [
1.1, 0.05, 0, 0.08,
0, 1.05, 0.05, 0.08,
0, 0, 0.95, 0.08,
0, 0, 0, 1
];
this.applyColorMatrix(imageData, warmMatrix);
// 彩度とコントラスト
this.adjustSaturation(imageData, 1.2);
this.adjustContrast(imageData, 1.08);
ctx.putImageData(imageData, 0, 0);
}
// X-Pro II フィルター
xpro2(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// カラーカーブ調整
this.applyCurves(imageData, {
red: this.createCurve([0, 0], [100, 150], [255, 255]),
green: this.createCurve([0, 0], [100, 100], [255, 255]),
blue: this.createCurve([0, 30], [100, 80], [255, 255])
});
// 強いヴィネット
this.addVignette(canvas, 0.6);
ctx.putImageData(imageData, 0, 0);
}
// ヘルパー関数
adjustContrast(imageData, factor) {
const data = imageData.data;
factor = (factor * 255 - 255) / 255;
for (let i = 0; i < data.length; i += 4) {
data[i] = data[i] + (data[i] - 128) * factor;
data[i + 1] = data[i + 1] + (data[i + 1] - 128) * factor;
data[i + 2] = data[i + 2] + (data[i + 2] - 128) * factor;
}
}
adjustSaturation(imageData, saturation) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
data[i] = gray + (data[i] - gray) * saturation;
data[i + 1] = gray + (data[i + 1] - gray) * saturation;
data[i + 2] = gray + (data[i + 2] - gray) * saturation;
}
}
addVignette(canvas, strength = 0.4) {
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const gradient = ctx.createRadialGradient(
width / 2, height / 2, 0,
width / 2, height / 2, Math.sqrt(width * width + height * height) / 2
);
gradient.addColorStop(0, `rgba(0,0,0,0)`);
gradient.addColorStop(0.5, `rgba(0,0,0,${strength * 0.2})`);
gradient.addColorStop(1, `rgba(0,0,0,${strength})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
}
}
2.2 VSCOスタイルフィルター
フィルムエミュレーション
class VSCOFilters {
// フィルムエミュレーション
filmEmulation(canvas, filmType) {
const films = {
'Fuji Velvia': {
curves: {
red: [[0, 0], [64, 56], [128, 128], [192, 196], [255, 255]],
green: [[0, 0], [64, 61], [128, 128], [192, 194], [255, 255]],
blue: [[0, 0], [64, 68], [128, 128], [192, 188], [255, 255]]
},
saturation: 1.4,
contrast: 1.2
},
'Kodak Portra': {
curves: {
red: [[0, 0], [64, 70], [128, 135], [192, 195], [255, 255]],
green: [[0, 0], [64, 65], [128, 128], [192, 190], [255, 255]],
blue: [[0, 0], [64, 62], [128, 124], [192, 188], [255, 245]]
},
saturation: 0.9,
contrast: 1.05
},
'Ilford HP5': {
grayscale: true,
curves: {
all: [[0, 10], [64, 58], [128, 134], [192, 200], [255, 250]]
},
grain: true,
contrast: 1.3
}
};
const film = films[filmType];
if (!film) return;
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
if (film.grayscale) {
this.toGrayscale(imageData);
}
this.applyCurvesAdjustment(imageData, film.curves);
if (film.saturation) {
this.adjustSaturation(imageData, film.saturation);
}
if (film.contrast) {
this.adjustContrast(imageData, film.contrast);
}
if (film.grain) {
this.addFilmGrain(imageData);
}
ctx.putImageData(imageData, 0, 0);
}
// フィルムグレイン効果
addFilmGrain(imageData, intensity = 0.1) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const noise = (Math.random() - 0.5) * intensity * 255;
data[i] = Math.max(0, Math.min(255, data[i] + noise));
data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise));
data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise));
}
}
// カーブ調整
applyCurvesAdjustment(imageData, curves) {
const data = imageData.data;
const lookupTables = {};
// ルックアップテーブルの作成
for (const channel in curves) {
lookupTables[channel] = this.createLookupTable(curves[channel]);
}
for (let i = 0; i < data.length; i += 4) {
if (lookupTables.red) {
data[i] = lookupTables.red[data[i]];
}
if (lookupTables.green) {
data[i + 1] = lookupTables.green[data[i + 1]];
}
if (lookupTables.blue) {
data[i + 2] = lookupTables.blue[data[i + 2]];
}
if (lookupTables.all) {
data[i] = lookupTables.all[data[i]];
data[i + 1] = lookupTables.all[data[i + 1]];
data[i + 2] = lookupTables.all[data[i + 2]];
}
}
}
createLookupTable(points) {
const lut = new Uint8Array(256);
// スプライン補間
for (let i = 0; i < 256; i++) {
lut[i] = this.cubicSplineInterpolate(points, i);
}
return lut;
}
}
第3章:アート効果フィルター
3.1 油絵効果
油絵風レンダリング
class ArtisticFilters {
oilPainting(canvas, brushSize = 4, intensity = 20) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = imageData.width;
const height = imageData.height;
const output = new Uint8ClampedArray(data);
for (let y = brushSize; y < height - brushSize; y++) {
for (let x = brushSize; x < width - brushSize; x++) {
const intensityBins = new Array(intensity + 1).fill(null).map(() => ({
r: 0, g: 0, b: 0, count: 0
}));
// 周辺ピクセルを強度ビンに分類
for (let dy = -brushSize; dy <= brushSize; dy++) {
for (let dx = -brushSize; dx <= brushSize; dx++) {
const idx = ((y + dy) * width + (x + dx)) * 4;
// 強度を計算
const intensity = Math.floor(
((data[idx] + data[idx + 1] + data[idx + 2]) / 3) *
intensity / 255
);
intensityBins[intensity].r += data[idx];
intensityBins[intensity].g += data[idx + 1];
intensityBins[intensity].b += data[idx + 2];
intensityBins[intensity].count++;
}
}
// 最も頻度の高い強度ビンを選択
let maxBin = intensityBins[0];
for (const bin of intensityBins) {
if (bin.count > maxBin.count) {
maxBin = bin;
}
}
const outputIdx = (y * width + x) * 4;
if (maxBin.count > 0) {
output[outputIdx] = maxBin.r / maxBin.count;
output[outputIdx + 1] = maxBin.g / maxBin.count;
output[outputIdx + 2] = maxBin.b / maxBin.count;
output[outputIdx + 3] = 255;
}
}
}
imageData.data.set(output);
ctx.putImageData(imageData, 0, 0);
}
// 水彩画効果
watercolor(canvas) {
const ctx = canvas.getContext('2d');
// ステップ1: エッジ保持平滑化
this.bilateralFilter(canvas);
// ステップ2: 色の単純化
this.quantizeColors(canvas, 8);
// ステップ3: 紙テクスチャの追加
this.addPaperTexture(canvas);
// ステップ4: エッジの強調
const edges = this.detectEdges(canvas);
ctx.globalCompositeOperation = 'multiply';
ctx.drawImage(edges, 0, 0);
}
// ペン画効果
penSketch(canvas) {
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// グレースケール変換
const imageData = ctx.getImageData(0, 0, width, height);
this.toGrayscale(imageData);
ctx.putImageData(imageData, 0, 0);
// エッジ検出
const edges = this.sobelEdgeDetection(canvas);
// ハッチング効果
const hatching = this.createHatching(edges);
// 合成
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, width, height);
ctx.drawImage(hatching, 0, 0);
}
createHatching(edgeCanvas) {
const ctx = edgeCanvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, edgeCanvas.width, edgeCanvas.height);
const data = imageData.data;
const hatchCanvas = document.createElement('canvas');
hatchCanvas.width = edgeCanvas.width;
hatchCanvas.height = edgeCanvas.height;
const hatchCtx = hatchCanvas.getContext('2d');
hatchCtx.strokeStyle = 'black';
hatchCtx.lineWidth = 0.5;
for (let y = 0; y < edgeCanvas.height; y += 2) {
for (let x = 0; x < edgeCanvas.width; x += 2) {
const idx = (y * edgeCanvas.width + x) * 4;
const brightness = data[idx] / 255;
if (brightness < 0.8) {
const lineCount = Math.floor((1 - brightness) * 4);
for (let i = 0; i < lineCount; i++) {
const angle = (brightness * Math.PI) + (i * Math.PI / 4);
const dx = Math.cos(angle) * 2;
const dy = Math.sin(angle) * 2;
hatchCtx.beginPath();
hatchCtx.moveTo(x - dx, y - dy);
hatchCtx.lineTo(x + dx, y + dy);
hatchCtx.stroke();
}
}
}
}
return hatchCanvas;
}
}
3.2 ポップアート効果
Andy Warhol風エフェクト
class PopArtFilter {
warholEffect(canvas, colors = 4) {
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// ポスタリゼーション
const posterized = this.posterize(canvas, colors);
// カラーパレット
const palettes = [
['#FF00FF', '#00FFFF', '#FFFF00', '#000000'],
['#FF0000', '#00FF00', '#0000FF', '#FFFFFF'],
['#FFA500', '#FF1493', '#00CED1', '#FFD700'],
['#8A2BE2', '#32CD32', '#FF69B4', '#1E90FF']
];
// 4分割グリッド作成
const gridCanvas = document.createElement('canvas');
gridCanvas.width = width * 2;
gridCanvas.height = height * 2;
const gridCtx = gridCanvas.getContext('2d');
for (let row = 0; row < 2; row++) {
for (let col = 0; col < 2; col++) {
const palette = palettes[row * 2 + col];
const colored = this.applyColorPalette(posterized, palette);
gridCtx.drawImage(
colored,
col * width,
row * height,
width,
height
);
}
}
// 元のキャンバスにリサイズして描画
ctx.drawImage(gridCanvas, 0, 0, width, height);
}
posterize(canvas, levels) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const factor = 255 / (levels - 1);
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.round(data[i] / factor) * factor;
data[i + 1] = Math.round(data[i + 1] / factor) * factor;
data[i + 2] = Math.round(data[i + 2] / factor) * factor;
}
const tempCanvas = document.createElement('canvas');
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
tempCanvas.getContext('2d').putImageData(imageData, 0, 0);
return tempCanvas;
}
applyColorPalette(canvas, palette) {
const tempCanvas = document.createElement('canvas');
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
const ctx = tempCanvas.getContext('2d');
ctx.drawImage(canvas, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
const index = Math.floor(gray * (palette.length - 1) / 255);
const color = this.hexToRgb(palette[index]);
data[i] = color.r;
data[i + 1] = color.g;
data[i + 2] = color.b;
}
ctx.putImageData(imageData, 0, 0);
return tempCanvas;
}
hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
}
第4章:WebGLによる高速フィルター処理
4.1 GPUアクセラレーション
WebGLシェーダーでの実装
class WebGLFilters {
constructor(canvas) {
this.gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
this.setupShaders();
}
setupShaders() {
// 頂点シェーダー
const vertexShaderSource = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vec4(a_position, 0, 1);
v_texCoord = a_texCoord;
}
`;
// フラグメントシェーダー(ブラー効果)
const blurFragmentShader = `
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_textureSize;
uniform float u_blurRadius;
varying vec2 v_texCoord;
void main() {
vec2 onePixel = vec2(1.0, 1.0) / u_textureSize;
vec4 colorSum = vec4(0.0);
float weightSum = 0.0;
for (float x = -u_blurRadius; x <= u_blurRadius; x += 1.0) {
for (float y = -u_blurRadius; y <= u_blurRadius; y += 1.0) {
float weight = exp(-(x*x + y*y) / (2.0 * u_blurRadius * u_blurRadius));
colorSum += texture2D(u_image, v_texCoord + onePixel * vec2(x, y)) * weight;
weightSum += weight;
}
}
gl_FragColor = colorSum / weightSum;
}
`;
this.blurProgram = this.createProgram(vertexShaderSource, blurFragmentShader);
}
createProgram(vertexSource, fragmentSource) {
const gl = this.gl;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
return program;
}
applyFilter(image, filterType, params) {
const gl = this.gl;
// テクスチャ作成
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// プログラム使用
gl.useProgram(this.blurProgram);
// ユニフォーム設定
const textureSizeLocation = gl.getUniformLocation(this.blurProgram, 'u_textureSize');
gl.uniform2f(textureSizeLocation, image.width, image.height);
const blurRadiusLocation = gl.getUniformLocation(this.blurProgram, 'u_blurRadius');
gl.uniform1f(blurRadiusLocation, params.radius || 5.0);
// 描画
this.render();
}
render() {
const gl = this.gl;
// ビューポート設定
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// クリア
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 描画
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
}
第5章:カスタムフィルターの作成
5.1 ユーザー定義フィルター
カスタムフィルタービルダー
class CustomFilterBuilder {
constructor() {
this.pipeline = [];
}
addStep(operation, params) {
this.pipeline.push({ operation, params });
return this;
}
brightness(value) {
return this.addStep('brightness', { value });
}
contrast(value) {
return this.addStep('contrast', { value });
}
saturation(value) {
return this.addStep('saturation', { value });
}
hue(degrees) {
return this.addStep('hue', { degrees });
}
colorMatrix(matrix) {
return this.addStep('colorMatrix', { matrix });
}
curves(curveData) {
return this.addStep('curves', { curveData });
}
blend(blendImage, mode, opacity) {
return this.addStep('blend', { blendImage, mode, opacity });
}
apply(canvas) {
const ctx = canvas.getContext('2d');
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (const step of this.pipeline) {
imageData = this.executeStep(imageData, step);
}
ctx.putImageData(imageData, 0, 0);
}
executeStep(imageData, step) {
const operations = {
brightness: (data, params) => {
const factor = params.value;
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.min(255, data[i] * factor);
data[i + 1] = Math.min(255, data[i + 1] * factor);
data[i + 2] = Math.min(255, data[i + 2] * factor);
}
},
contrast: (data, params) => {
const factor = (params.value + 100) / 100;
const intercept = 128 * (1 - factor);
for (let i = 0; i < data.length; i += 4) {
data[i] = data[i] * factor + intercept;
data[i + 1] = data[i + 1] * factor + intercept;
data[i + 2] = data[i + 2] * factor + intercept;
}
},
hue: (data, params) => {
const angle = params.degrees * Math.PI / 180;
const cos = Math.cos(angle);
const sin = Math.sin(angle);
for (let i = 0; i < data.length; i += 4) {
const [h, s, l] = this.rgbToHsl(data[i], data[i + 1], data[i + 2]);
const newHue = (h + params.degrees / 360) % 1;
const [r, g, b] = this.hslToRgb(newHue, s, l);
data[i] = r;
data[i + 1] = g;
data[i + 2] = b;
}
}
};
const operation = operations[step.operation];
if (operation) {
operation(imageData.data, step.params);
}
return imageData;
}
// 色空間変換ヘルパー
rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
if (max === min) {
return [0, 0, l];
}
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h;
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
return [h, s, l];
}
hslToRgb(h, s, l) {
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
}
// 使用例
const customFilter = new CustomFilterBuilder()
.brightness(1.2)
.contrast(1.1)
.saturation(1.3)
.hue(10)
.colorMatrix([
1.1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 0.9, 0,
0, 0, 0, 1
])
.apply(canvas);
Security and Privacy
All processing is done within your browser, and no data is sent externally. You can safely use it with personal or confidential information.
Troubleshooting
Common Issues
- Not working: Clear browser cache and reload
- Slow processing: Check file size (recommended under 20MB)
- Unexpected results: Verify input format and settings
If issues persist, update your browser to the latest version or try a different browser.
まとめ:プロフェッショナルな画像フィルター活用
画像フィルターは、写真を芸術作品に変換する強力なツールです。以下のポイントを押さえることで、効果的な実装を実現できます:
- 基礎理論の理解:カラーマトリックスと畳み込みフィルター
- 人気エフェクトの実装:Instagram風、VSCO風フィルター
- アート効果の活用:油絵、水彩、ペン画効果
- パフォーマンス最適化:WebGLによるGPUアクセラレーション
- カスタマイズ性:ユーザー定義フィルターの作成
i4uの画像フィルターツールを活用することで、簡単にプロ級のエフェクトを適用できます。
カテゴリ別ツール
他のツールもご覧ください:
関連ツール
Related Posts
Complete OCR Tool Guide 2025|High-Precision Text Extraction from Images
Extract text from images and PDFs instantly. High-precision OCR tool supporting Japanese, English, Chinese, and Korean. Perfect for digitizing business cards, documents, and scanned files. Browser-based processing ensures privacy protection.
2025年最新!AIブログアイデアジェネレーターの選び方と活用法Complete Guide
ブログのネタ切れに悩むあなたへ。AIブログアイデアジェネレーターを使って無限のコンテンツアイデアを生み出す方法を、実例とともに徹底解説します。
2025年最新!AI画像アップスケーラーComplete Guide|低解像度画像を高画質化する方法
古い写真や低解像度画像を最新のAI技術で高画質化。無料で使えるi4u AI画像アップスケーラーの使い方から、プロレベルの活用テクニックまで徹底解説します。