convertTo() 메서드 이해하기
OpenCV의 org.opencv.core.Mat 클래스가 제공하는 convertTo() 메서드를 사용하면 이미지의 밝기와 대비를 아주 간단하게 조절할 수 있습니다. 이 메서드는 순서대로 다음 4개의 매개변수를 받습니다.
m – 변환 결과를 저장할 대상 행렬(빈 행렬)
rtype – 출력 행렬의 타입(-1을 지정하면 원본과 동일한 타입 사용)
alpha – 각 픽셀에 곱해지는 계수(gain) 값
beta – 곱셈 결과에 더해지는 오프셋 값
내부적으로 이 메서드는 dst = alpha × src + beta 공식에 따라 모든 픽셀 값을 일괄 변환합니다. 따라서 alpha는 대비(픽셀 값 분포의 확산 정도)를, beta는 밝기(전체적인 명암 이동)를 담당한다고 볼 수 있습니다.
밝기를 높이려면 − alpha 값을 1로 유지한 상태에서 beta 값을 0에서 양의 방향(최대 +255)으로 증가시킵니다.
밝기를 낮추려면 − alpha 값을 1로 유지한 상태에서 beta 값을 0에서 음의 방향(최소 -255)으로 감소시킵니다.
대비를 높이려면 − beta 값을 0으로 유지한 상태에서 alpha 값을 1보다 큰 값(필요에 따라 최대 100까지)으로 증가시킵니다.
대비를 낮추려면 − beta 값을 0으로 유지한 상태에서 alpha 값을 1에서 0 사이의 값으로 감소시킵니다.
예제 코드
다음은 두 개의 슬라이더 바를 사용해 alpha와 beta 값을 실시간으로 변경하는 JavaFX 프로그램입니다. 첫 번째 슬라이더는 대비(alpha)를, 두 번째 슬라이더는 밝기(beta)를 조절하며, 값이 바뀔 때마다 convertTo() 메서드로 이미지를 새로 변환해 화면에 표시합니다.
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 AlphaBetaValuesJavaFX extends Application {
double contrast = 1;
private final int rtype = -1;
double alpha = 1;
double beta = 0;
Slider slider1;
Slider slider2;
Mat src = null;
public void start(Stage stage) throws IOException {
// OpenCV 핵심 네이티브 라이브러리 로드
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
String file = "D:\\Image\\lamma2.jpg";
src = Imgcodecs.imread(file);
WritableImage writableImage = loadImage(src);
// 이미지 뷰 설정
ImageView imageView = new ImageView(writableImage);
imageView.setX(50);
imageView.setY(25);
imageView.setFitHeight(400);
imageView.setFitWidth(550);
imageView.setPreserveRatio(true);
Label label1 = new Label("Alpha 값");
// 대비 조절용 슬라이더 설정
slider1 = new Slider(0.1, 3, 1);
slider1.setShowTickLabels(true);
slider1.setShowTickMarks(true);
slider1.setMajorTickUnit(1);
slider1.setBlockIncrement(0.05);
Label label2 = new Label();
Label label3 = new Label("Beta 값");
// 밝기 조절용 슬라이더 설정
slider2 = new Slider(-100, 100, 0);
slider2.setShowTickLabels(true);
slider2.setShowTickMarks(true);
slider2.setMajorTickUnit(25);
slider2.setBlockIncrement(10);
Label label4 = new Label();
// alpha 슬라이더 리스너
slider1.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
try {
label2.setText("α-value: " + newValue);
alpha = newValue.doubleValue();
Mat dest = new Mat(src.rows(), src.cols(), src.type());
src.convertTo(dest, rtype, alpha, beta);
imageView.setImage(loadImage(dest));
} catch (Exception e) {
System.out.println("");
}
}
});
// beta 슬라이더 리스너
slider2.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
try {
label4.setText("β-value: " + newValue);
beta = newValue.doubleValue();
Mat dest = new Mat(src.rows(), src.cols(), src.type());
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, slider1, label2, imageView, label3, slider2, label4);
// 씬(Scene) 생성 및 표시
Scene scene = new Scene(vbox, 600, 580);
stage.setTitle("Loading 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 배열로 저장
byte[] byteArray = matOfByte.toArray();
// 이미지 표시
InputStream in = new ByteArrayInputStream(byteArray);
BufferedImage bufImage = ImageIO.read(in);
System.out.println("Image Loaded");
WritableImage writableImage = SwingFXUtils.toFXImage(bufImage, null);
return writableImage;
}
public static void main(String args[]) {
launch(args);
}
}
동작 방식 살펴보기
프로그램의 흐름은 다음과 같습니다.
이미지 로드 – Imgcodecs.imread()로 파일을 읽어 Mat 객체에 저장합니다.
슬라이더 연동 – 각 슬라이더에 ChangeListener를 등록해 값이 변경될 때마다 alpha 또는 beta 변수를 갱신합니다.
이미지 변환 – src.convertTo(dest, rtype, alpha, beta)를 호출해 새로운 밝기·대비가 적용된 행렬을 생성합니다.
화면 출력 – loadImage() 메서드가 Mat 객체를 JPEG로 인코딩한 뒤 BufferedImage로 변환하고, SwingFXUtils.toFXImage()를 통해 JavaFX에서 표시 가능한 WritableImage로 변환하여 ImageView에 반영합니다.
입력 이미지

출력 결과
