2016-08-19 2 views
0

Je travaille actuellement sur un programme pour aider les photographes à créer des timelapses. Il calcule une baisse ou une augmentation de la luminosité sur une série d'images. Ainsi, le changement d'exposition et d'iso par exemple n'affectent pas la baisse globale de la luminosité. Pour cela, j'utilise une interface simple basée sur Swing qui affiche la première et la dernière image. Sous eux sont des curseurs pour ajuster la luminosité de l'image.Multiplier les valeurs de pixels dans BufferedImage entraîne un comportement étrange

Ceci est appliqué via une manipulation directe du DataBuffer sous-jacent BufferedImages.

Généralement cela fonctionne mais j'ai rencontré quelques images qui semblent avoir un problème.

Avez-vous une idée pourquoi cela se produit?

public BufferedImage getImage(float mult){ 
    BufferedImage retim; 
    retim = new BufferedImage(img.getWidth(), img.getHeight(), img.getType()); 
    Graphics g = retim.getGraphics(); 
    g.drawImage(img, 0, 0, null); 
    g.dispose(); 

    DataBufferByte db = (DataBufferByte) retim.getRaster().getDataBuffer(); 
    byte[] bts = db.getData(); 
    for(int i=0;i<bts.length;i++){ 
     float n = bts[i]*mult; 
     if(n > 255){ 
      bts[i]= (byte) 255; 
     }else{ 
      bts[i] = (byte) n; 
     } 
    } 
    return retim; 
} 

Ceci est la méthode qui prend un flottant et multiplie chaque pixel de l'image avec lui. (Et du code pour empêcher les valeurs d'octets de déborder).

This is the unwanted behaviour (on the left) and the expected on the right.

Répondre

0

Votre problème est cette ligne, et il se produit en raison du fait que Java byte s sont signés (dans la gamme [-128 ... 127]):

float n = bts[i] * mult; 

Après la multiplication, votre n La variable peut être négative, provoquant ainsi le débordement.

Pour résoudre ce problème, en utilisant un masque de bits pour obtenir la valeur comme un entier non signé (dans l'intervalle [0 ... 255]), avant la multiplication par la constante:

float n = (bts[i] & 0xff) * mult; 

A mieux fixer encore, est probablement d'utiliser le RescaleOp, qui est construit pour faire des ajustements de luminosité sur BufferedImage s.

Quelque chose comme:

public BufferedImage getImage(float mult) { 
    return new RescaleOp(mult, 0, null).filter(img, null); 
} 
+1

Merci beaucoup!J'ai déjà essayé d'implémenter une pseudo-version non signée de l'octet java pour surmonter cela, mais en utilisant simplement l'option «rescale», ça fonctionne beaucoup mieux;) – I4k

0

Cela est dû au plafonnement de la valeur dans certains octets de l'image.

Par exemple (en supposant l'espace colorimétrique RVB simple requise):

Le pixel commence à (125,255,0), si vous multipliez par le facteur 2.0, le résultat est (255,255,0). C'est une teinte différente de l'original.

C'est aussi pourquoi les résultats étranges ne se produisent que sur les pixels qui ont déjà une luminosité élevée pour commencer.

Ce link peut aider avec un meilleur algorithme pour ajuster la luminosité. Vous pouvez également vous référer au this related question.

+0

Merci de votre suggestion. Le papier que vous avez pointé semble ne plus être disponible mais j'ai réussi à le trouver via le waybackmachine. Bien qu'il semble que la méthode proposée dans ce document est quelque chose que je fais déjà, à savoir la multiplication des valeurs de pixels. – I4k