soiz1's picture
Update templates/index.html
b05b1fc verified
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RGBYカーブエディタ</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div class="container">
<h1>RGBYカーブエディタ</h1>
<form method="post" enctype="multipart/form-data" onsubmit="showLoading()">
<div class="upload-section">
<input type="file" id="fileInput" accept=".jpg,.jpeg,.png">
<button id="uploadBtn">画像を読み込み</button>
<div id="loading" style="display:none;">処理中...</div>
</div>
<div class="curve-controls">
<h2>2Dトーンカーブエディタ</h2>
<div class="curve-editor-container">
<canvas id="curveCanvas" width="400" height="400"></canvas>
<div class="curve-presets">
<button type="button" onclick="setLinearCurve()">リニア</button>
<button type="button" onclick="setContrastCurve()">コントラスト↑</button>
<button type="button" onclick="setInverseCurve()">反転</button>
</div>
</div>
<div class="channel-controls">
<div class="channel">
<h3>Redチャンネル</h3>
<div class="sliders">
{% for i in range(1, 6) %}
<div class="slider-container">
<label>ポイント{{ i }}</label>
<input type="range" min="0" max="255" value="{{ r_curve[i-1] }}"
name="r{{ i }}" id="r{{ i }}" class="slider" oninput="updateCurve()">
<span class="slider-value">{{ r_curve[i-1] }}</span>
</div>
{% endfor %}
</div>
</div>
<div class="channel">
<h3>Greenチャンネル</h3>
<div class="sliders">
{% for i in range(1, 6) %}
<div class="slider-container">
<label>ポイント{{ i }}</label>
<input type="range" min="0" max="255" value="{{ g_curve[i-1] }}"
name="g{{ i }}" id="g{{ i }}" class="slider" oninput="updateCurve()">
<span class="slider-value">{{ g_curve[i-1] }}</span>
</div>
{% endfor %}
</div>
</div>
<div class="channel">
<h3>Blueチャンネル</h3>
<div class="sliders">
{% for i in range(1, 6) %}
<div class="slider-container">
<label>ポイント{{ i }}</label>
<input type="range" min="0" max="255" value="{{ b_curve[i-1] }}"
name="b{{ i }}" id="b{{ i }}" class="slider" oninput="updateCurve()">
<span class="slider-value">{{ b_curve[i-1] }}</span>
</div>
{% endfor %}
</div>
</div>
<div class="channel">
<h3>明るさ (Y)</h3>
<div class="sliders">
{% for i in range(1, 6) %}
<div class="slider-container">
<label>ポイント{{ i }}</label>
<input type="range" min="0" max="255" value="{{ y_curve[i-1] }}"
name="y{{ i }}" id="y{{ i }}" class="slider" oninput="updateCurve()">
<span class="slider-value">{{ y_curve[i-1] }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</form>
<div class="image-comparison">
<div class="image-container">
<h2>元画像</h2>
<img id="originalImage" src="" alt="Original Image" style="display:none;">
<canvas id="originalCanvas"></canvas>
</div>
<div class="image-container">
<h2>調整後画像</h2>
<canvas id="adjustedCanvas"></canvas>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/curve_editor.js') }}"></script>
<script>
// グローバル変数
let originalImageData = null;
// DOM要素を取得
const fileInput = document.getElementById('fileInput');
const uploadBtn = document.getElementById('uploadBtn');
const originalImage = document.getElementById('originalImage');
const originalCanvas = document.getElementById('originalCanvas');
const adjustedCanvas = document.getElementById('adjustedCanvas');
const loadingIndicator = document.getElementById('loading');
// 画像アップロード処理
uploadBtn.addEventListener('click', async () => {
const file = fileInput.files[0];
if (!file) return;
try {
loadingIndicator.style.display = 'block';
// 画像を読み込み
const reader = new FileReader();
reader.onload = async (e) => {
originalImage.src = e.target.result;
originalImage.onload = async () => {
// Canvasに描画
drawImageToCanvas(originalImage, originalCanvas);
// 画像データを保存
originalImageData = await getImageData(originalCanvas);
// 初期処理を実行
await processImage();
loadingIndicator.style.display = 'none';
};
};
reader.readAsDataURL(file);
} catch (error) {
console.error('Error:', error);
loadingIndicator.style.display = 'none';
alert('画像の読み込みに失敗しました');
}
});
// 画像処理関数
async function processImage() {
if (!originalImageData) return;
try {
loadingIndicator.style.display = 'block';
// カーブパラメータを取得
const params = {
image: originalImageData,
r_curve: [
parseInt(document.getElementById('r1').value),
parseInt(document.getElementById('r2').value),
parseInt(document.getElementById('r3').value),
parseInt(document.getElementById('r4').value),
parseInt(document.getElementById('r5').value)
],
g_curve: [
parseInt(document.getElementById('g1').value),
parseInt(document.getElementById('g2').value),
parseInt(document.getElementById('g3').value),
parseInt(document.getElementById('g4').value),
parseInt(document.getElementById('g5').value)
],
b_curve: [
parseInt(document.getElementById('b1').value),
parseInt(document.getElementById('b2').value),
parseInt(document.getElementById('b3').value),
parseInt(document.getElementById('b4').value),
parseInt(document.getElementById('b5').value)
],
y_curve: [
parseInt(document.getElementById('y1').value),
parseInt(document.getElementById('y2').value),
parseInt(document.getElementById('y3').value),
parseInt(document.getElementById('y4').value),
parseInt(document.getElementById('y5').value)
]
};
// APIを呼び出し
const response = await fetch('/api/process_image', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
const result = await response.json();
if (result.status === 'success') {
// 処理後の画像を表示
const img = new Image();
img.onload = () => {
drawImageToCanvas(img, adjustedCanvas);
loadingIndicator.style.display = 'none';
};
img.src = result.image;
} else {
throw new Error(result.message);
}
} catch (error) {
console.error('Error:', error);
loadingIndicator.style.display = 'none';
alert('画像処理に失敗しました');
}
}
// 画像をCanvasに描画
function drawImageToCanvas(image, canvas) {
const ctx = canvas.getContext('2d');
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
}
// Canvasから画像データを取得
async function getImageData(canvas) {
return new Promise((resolve) => {
canvas.toBlob((blob) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(blob);
}, 'image/jpeg', 0.9);
});
}
// スライダー変更時に画像処理を実行
document.querySelectorAll('.slider').forEach(slider => {
slider.addEventListener('input', function() {
const valueSpan = this.parentElement.querySelector('.slider-value');
valueSpan.textContent = this.value;
// デバウンス処理 (連続変更を防ぐ)
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(processImage, 300);
});
});
// 初期化
document.addEventListener('DOMContentLoaded', function() {
// スライダーの値を表示
document.querySelectorAll('.slider').forEach(slider => {
const valueSpan = slider.parentElement.querySelector('.slider-value');
valueSpan.textContent = slider.value;
});
// カーブエディタを初期化
initCurveEditor();
// カーブエディタの変更時に処理を実行
window.updateCurve = function() {
processImage();
};
});
</script>
</body>
</html>