| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- function generateHistogram(imageElement, canvas) {
- try {
- // Check if image is loaded and has valid dimensions
- if (!imageElement.complete || imageElement.naturalWidth === 0) {
- console.log('Image not fully loaded yet');
- // Retry after a short delay
- setTimeout(() => generateHistogram(imageElement, canvas), 100);
- return;
- }
- console.log('Generating histogram for image:', imageElement.naturalWidth, 'x', imageElement.naturalHeight);
- // Create a temporary canvas to get image data
- const tempCanvas = document.createElement('canvas');
- const tempCtx = tempCanvas.getContext('2d');
- tempCanvas.width = imageElement.naturalWidth;
- tempCanvas.height = imageElement.naturalHeight;
- // Draw the image to the temp canvas
- tempCtx.drawImage(imageElement, 0, 0);
- // Get image data
- const imageData = tempCtx.getImageData(0, 0, imageElement.naturalWidth, imageElement.naturalHeight);
- const { width, height, data } = imageData;
- const totalPixels = width * height;
- const numSamples = Math.min(10000, totalPixels); // sample up to 10,000 pixels
- // Initialize histogram arrays for R, G, B
- const histR = new Array(256).fill(0);
- const histG = new Array(256).fill(0);
- const histB = new Array(256).fill(0);
- // Sample pixels
- let sampleCount = 0;
- for (let s = 0; s < numSamples; s++) {
- const i = Math.floor(Math.random() * width);
- const j = Math.floor(Math.random() * height);
- const index = (j * width + i) * 4;
- const r = data[index];
- const g = data[index + 1];
- const b = data[index + 2];
- histR[r]++;
- histG[g]++;
- histB[b]++;
- sampleCount++;
- }
- console.log('Sampled', sampleCount, 'pixels for histogram');
- // Draw histogram on canvas
- const ctx = canvas.getContext('2d');
- const canvasWidth = canvas.width;
- const canvasHeight = canvas.height;
- ctx.clearRect(0, 0, canvasWidth, canvasHeight);
- // Find max value for scaling
- const maxR = Math.max(...histR.slice(1, 255));
- const maxG = Math.max(...histG.slice(1, 255));
- const maxB = Math.max(...histB.slice(1, 255));
- const maxVal = Math.max(maxR, maxG, maxB);
- if (maxVal === 0) {
- // No data to display
- ctx.fillStyle = '#666';
- ctx.font = '12px Arial';
- ctx.textAlign = 'center';
- ctx.fillText('No histogram data', canvasWidth / 2, canvasHeight / 2);
- console.log('No histogram data to display');
- return;
- }
- const scale = canvasHeight / maxVal;
- // Draw bars for each channel with some transparency
- for (let i = 1; i < 255; i+=2) {
- // Red channel
- ctx.fillStyle = 'rgba(255, 100, 100, 0.7)';
- ctx.fillRect(i * (canvasWidth / 256), canvasHeight - histR[i] * scale, canvasWidth / 256, histR[i] * scale);
- // Green channel
- ctx.fillStyle = 'rgba(100, 255, 100, 0.7)';
- ctx.fillRect(i * (canvasWidth / 256), canvasHeight - histG[i] * scale, canvasWidth / 256, histG[i] * scale);
- // Blue channel
- ctx.fillStyle = 'rgba(100, 100, 255, 0.7)';
- ctx.fillRect(i * (canvasWidth / 256), canvasHeight - histB[i] * scale, canvasWidth / 256, histB[i] * scale);
- }
- // Add a subtle border
- ctx.strokeStyle = '#555';
- ctx.lineWidth = 1;
- ctx.strokeRect(0, 0, canvasWidth, canvasHeight);
- console.log('Histogram generated successfully');
- } catch (error) {
- console.error('Error generating histogram:', error);
- const ctx = canvas.getContext('2d');
- const canvasWidth = canvas.width;
- const canvasHeight = canvas.height;
- ctx.clearRect(0, 0, canvasWidth, canvasHeight);
- ctx.fillStyle = '#666';
- ctx.font = '12px Arial';
- ctx.textAlign = 'center';
- ctx.fillText('Histogram unavailable', canvasWidth / 2, canvasHeight / 2);
- }
- }
- function analysis_tone_types(imageElement, callback) {
- try {
- // Check if image is loaded and has valid dimensions
- if (!imageElement.complete || imageElement.naturalWidth === 0) {
- console.log('Image not fully loaded yet');
- // Retry after a short delay
- setTimeout(() => analysis_tone_types(imageElement, callback), 100);
- return;
- }
- console.log('Analyzing tone for image:', imageElement.naturalWidth, 'x', imageElement.naturalHeight);
- // Create a temporary canvas to get image data
- const tempCanvas = document.createElement('canvas');
- const tempCtx = tempCanvas.getContext('2d');
- tempCanvas.width = imageElement.naturalWidth;
- tempCanvas.height = imageElement.naturalHeight;
- // Draw the image to the temp canvas
- tempCtx.drawImage(imageElement, 0, 0);
- // Get image data
- const imageData = tempCtx.getImageData(0, 0, imageElement.naturalWidth, imageElement.naturalHeight);
- const { width, height, data } = imageData;
- const totalPixels = width * height;
- // Thresholds for shadows and highlights (in luminance 0-255)
- const shadowThreshold = 0.1 * 255; // 10%
- const highlightThreshold = 0.9 * 255; // 90%
- let totalLuminance = 0;
- let minLum = 255;
- let maxLum = 0;
- let shadowCount = 0;
- let highlightCount = 0;
- // Calculate luminance for each pixel
- for (let i = 0; i < data.length; i += 4) {
- const r = data[i];
- const g = data[i + 1];
- const b = data[i + 2];
- const lum = 0.299 * r + 0.587 * g + 0.114 * b;
- totalLuminance += lum;
- if (lum < minLum) minLum = lum;
- if (lum > maxLum) maxLum = lum;
- if (lum < shadowThreshold) shadowCount++;
- if (lum > highlightThreshold) highlightCount++;
- }
- // Brightness: average luminance as percentage
- const avgLuminance = totalLuminance / totalPixels;
- const brightness = (avgLuminance / 255) * 100;
- // Contrast: range-based as percentage
- const contrast = ((maxLum - minLum) / 255) * 100;
- // Shadow and highlight ratios
- const shadowRatio = (shadowCount / totalPixels) * 100;
- const highlightRatio = (highlightCount / totalPixels) * 100;
- // Return results to callback
- callback({
- brightness: Math.round(brightness) + "%",
- contrast: Math.round(contrast) + "%",
- shadowRatio: Math.round(shadowRatio) + "%",
- highlightRatio: Math.round(highlightRatio) + "%"
- });
- console.log('Tone analysis completed');
- } catch (error) {
- console.error('Error in analysis_tone_types:', error);
- callback(null);
- }
- }
- function get_tone_type(brightness, contrast, shadowRatio, highlightRatio) {
- const b = parseFloat(brightness);
- const c = parseFloat(contrast);
- const s = parseFloat(shadowRatio);
- const h = parseFloat(highlightRatio);
- // Classify based on brightness (Key)
- let key;
- if (b > 65) {
- key = "High Key";
- } else if (b < 35) {
- key = "Low Key";
- } else {
- key = "Middle Key";
- }
- // Classify based on contrast (Scale)
- let scale;
- if (c > 70) {
- scale = "Long Scale";
- } else if (c < 30) {
- scale = "Short Scale";
- } else {
- scale = "Middle Scale";
- }
- return key + " " + scale;
- }
|