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

OpenCV와 JavaFX로 구현하는 이미지 선명도 조절 애플리케이션

이미지 선명화(Sharpening)는 블러 처리와 정반대되는 개념입니다. OpenCV 라이브러리를 사용해 이미지의 선명도를 조절하려면 먼저 가우시안 필터(Gaussian Filter)를 적용해 이미지를 부드럽게 만든 뒤, 원본 이미지에서 이 흐려진 버전을 빼주는 방식으로 구현합니다. 이 과정에서 Core.addWeighted() 메서드를 활용하면 알파(alpha)와 베타(beta) 가중치를 통해 선명도를 자유롭게 제어할 수 있습니다.

예제 코드

다음은 알파 값과 베타 값을 각각 조절할 수 있는 두 개의 슬라이더를 포함한 JavaFX 프로그램 예제입니다. 슬라이더를 움직이면 실시간으로 이미지의 선명도가 변경됩니다.

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.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
public class SharpnessJavaFX 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 value");
        // 첫 번째 슬라이더 설정 (알파 값)
        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 value");
        // 두 번째 슬라이더 설정 (베타 값)
        slider2 = new Slider(-2, 2, 0);
        slider2.setShowTickLabels(true);
        slider2.setShowTickMarks(true);
        slider2.setMajorTickUnit(1);
        slider2.setBlockIncrement(.5);
        // 라벨 설정
        Label label4 = new Label();
        // 첫 번째 슬라이더 리스너
        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());
                    Imgproc.GaussianBlur(src, dest, new Size(0,0), 10);
                    Core.addWeighted(src, alpha, dest, beta, 0, dest);
                    imageView.setImage(loadImage(dest));
                }
                catch(Exception e) {
                    System.out.println("");
                }
            }
        });
        // 두 번째 슬라이더 리스너
        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());
                    Imgproc.GaussianBlur(src, dest, new Size(0,0), 10);
                    Core.addWeighted(src, alpha, dest, beta, 0, dest);
                    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, 560);
        stage.setTitle("Altering the sharpness");
        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);
        System.out.println("Image Loaded");
        WritableImage writableImage = SwingFXUtils.toFXImage(bufImage, null);
        return writableImage;
    }
    public static void main(String args[]) {
        launch(args);
    }
}

입력 이미지

OpenCV와 JavaFX로 구현하는 이미지 선명도 조절 애플리케이션

출력 결과

OpenCV와 JavaFX로 구현하는 이미지 선명도 조절 애플리케이션

핵심 동작 원리

이 프로그램의 선명도 조절 로직은 크게 세 단계로 이루어집니다.

1. 가우시안 블러 적용Imgproc.GaussianBlur() 메서드로 원본 이미지를 흐리게 만듭니다. 여기서는 시그마 값 10으로 강하게 블러링합니다.
2. 가중합 연산Core.addWeighted() 메서드로 원본과 블러된 이미지를 가중치 기반으로 합성합니다. 알파 값이 클수록 원본의 비중이 커져 이미지가 더 선명해 보이고, 베타 값은 블러 성분의 비중을 결정합니다.
3. 실시간 UI 반영 — JavaFX 슬라이더의 ChangeListener가 값 변화를 감지할 때마다 위 연산을 수행하고 화면을 갱신합니다.

이처럼 OpenCV의 행렬 연산 기능과 JavaFX의 인터랙티브 UI를 결합하면, 사용자가 직관적으로 선명도를 조절할 수 있는 데스크톱 이미지 편집 도구를 손쉽게 만들 수 있습니다.