Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

JavaFX와 OpenCV convertTo() 메서드로 이미지 밝기 줄이기 – 완전한 예제 가이드

OpenCV convertTo() 메서드로 이미지 밝기 조절하기

자바에서 이미지의 밝기를 변경하는 방법 중 하나는 OpenCV의 convertTo() 메서드를 활용하는 것입니다. 이 메서드는 주어진 행렬(Mat)에 필요한 연산을 수행하여 이미지의 대비(contrast)밝기(brightness)를 손쉽게 조절할 수 있게 해줍니다.

convertTo() 메서드는 다음과 같은 4개의 매개변수를 받습니다.

  • mat – 결과를 저장할 빈 행렬로, 원본 행렬과 동일한 크기와 타입을 가져야 합니다.

  • rtype – 출력 행렬의 타입을 지정하는 정수 값입니다. 음수를 지정하면 원본 행렬과 같은 타입이 사용됩니다.

  • alpha – 이득(gain) 값으로, 반드시 0보다 커야 합니다(기본값 1).

  • beta – 바이어스(bias) 값입니다(기본값 0).

beta 매개변수에 음수 값(0 ~ -255 범위)을 지정하면 이미지의 밝기가 감소합니다. 각 픽셀 값에서 일정량을 빼는 방식으로 어두운 톤의 이미지를 만들 수 있습니다.

전체 예제 코드

아래 예제는 JavaFX 애플리케이션에 슬라이더(Slider)를 배치하고, 슬라이더 값을 실시간으로 alpha 값에 반영하여 이미지 밝기를 조절하는 프로그램입니다. 슬라이더를 왼쪽으로 움직일수록 alpha 값이 작아지면서 이미지가 점점 어두워집니다.

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.imgcodecs.Imgcodecs;

public class DecreasingBrightnessJavaFX extends Application {
    double contrast = 1;
    private final int rtype = -1;
    double alpha = 1;
    double beta = 0;
    Slider slider1;
    int sliderMinVal = 0;
    int sliderMaxVal = 255;
    int sliderInitVal = 255;
    Mat src = null;

    public void start(Stage stage) throws IOException {
       // OpenCV 핵심 라이브러리 로드
       System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
       String file = "D:\\Image\\cuba.jpg";
       src = Imgcodecs.imread(file);
       WritableImage writableImage = loadImage(src);
       // 이미지 뷰 설정
       ImageView imageView = new ImageView(writableImage);
       imageView.setX(25);
       imageView.setY(25);
       imageView.setFitHeight(400);
       imageView.setFitWidth(550);
       imageView.setPreserveRatio(true);
       // 슬라이더 설정
       slider1 = new Slider(sliderMinVal, sliderMaxVal, sliderInitVal);
       slider1.setShowTickLabels(true);
       slider1.setShowTickMarks(true);
       slider1.setMajorTickUnit(25);
       slider1.setBlockIncrement(10);
       // 레이블 설정
       Label label1 = new Label();
       Label label2 = new Label();
       // 슬라이더 값 변경 리스너
       slider1.valueProperty().addListener(new ChangeListener<Number>() {
           public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
               try {
                   alpha = newValue.doubleValue();
                   Mat dest = new Mat(src.rows(), src.cols(), src.type());
                   alpha = (alpha / sliderMaxVal);
                   beta = 1.0 - alpha;
                   label1.setText("α-value: " + alpha);
                   label2.setText("β-value: " + beta);
                   src.convertTo(dest, rtype, alpha, beta);
                   imageView.setImage(loadImage(dest));
               } catch(Exception e) {
                   System.out.println("");
               }
           }
       });
       // 세 가지 구성 요소를 배치할 VBox
       VBox vbox = new VBox();
       vbox.setPadding(new Insets(20));
       vbox.setSpacing(10);
       vbox.getChildren().addAll(label1, label2, slider1, imageView);
       // 씬(Scene) 객체 생성
       Scene scene = new Scene(vbox, 600, 450);
       stage.setTitle("Decreasing an image");
       stage.setScene(scene);
       stage.show();
    }

    public WritableImage loadImage(Mat image) throws IOException {
       MatOfByte matOfByte = new MatOfByte();
       Imgcodecs.imencode(".jpg", image, matOfByte);
       // 인코딩된 Mat을 바이트 배열에 저장
       byte[] byteArray = matOfByte.toArray();
       // 이미지 표시
       InputStream in = new ByteArrayInputStream(byteArray);
       BufferedImage bufImage = ImageIO.read(in);
       WritableImage writableImage = SwingFXUtils.toFXImage(bufImage, null);
       return writableImage;
    }

    public static void main(String args[]) {
       launch(args);
    }
}

코드 주요 흐름

  1. 이미지 로드 – Imgcodecs.imread()로 이미지 파일을 Mat 객체로 읽어옵니다.

  2. UI 구성 – ImageView, Slider, Label을 VBox 레이아웃으로 배치하여 화면을 구성합니다.

  3. 슬라이더 리스너 등록 – 슬라이더 값이 변경될 때마다 alpha 값을 0~1 범위로 정규화하고, beta = 1 - alpha로 계산한 뒤 convertTo()를 호출합니다.

  4. 결과 표시 – 변환된 Mat을 JPEG로 인코딩한 후 BufferedImage를 거쳐 WritableImage로 변환하여 ImageView를 갱신합니다.

실행 결과

입력 이미지

JavaFX와 OpenCV convertTo() 메서드로 이미지 밝기 줄이기 – 완전한 예제 가이드

출력 이미지 (밝기 감소)

JavaFX와 OpenCV convertTo() 메서드로 이미지 밝기 줄이기 – 완전한 예제 가이드

이처럼 convertTo() 메서드 하나만으로도 별도의 픽셀 단위 연산 없이 이미지의 밝기와 대비를 간편하게 조절할 수 있습니다. 단, 실행 전에 System.loadLibrary()를 통해 OpenCV 네이티브 라이브러리가 올바르게 설정되어 있어야 한다는 점에 유의하세요.