개요
이미지 처리 작업에서 컬러 이미지를 그레이스케일(흑백)로 변환하는 일은 매우 기본적이면서도 자주 사용되는 기법입니다. 보통은 OpenCV 같은 전문 라이브러리를 활용하지만, Java 표준 라이브러리(java.awt, javax.imageio)만으로도 충분히 간단하게 구현할 수 있습니다. 이 글에서는 외부 라이브러리 없이 순수 Java 코드만으로 이미지를 그레이스케일로 변환하는 방법을 알아보겠습니다.
그레이스케일 변환의 원리
컬러 이미지는 각 픽셀이 빨강(Red), 초록(Green), 파랑(Blue) 세 가지 색상 값으로 이루어져 있습니다. 이를 그레이스케일로 바꾸려면 다음과 같은 단계를 거칩니다.
- 각 픽셀의 R(빨강), G(초록), B(파랑) 값을 추출합니다.
- 세 색상 값의 평균을 계산합니다.
- 기존 RGB 값을 계산된 평균값으로 대체합니다.
- 변경된 색상 값으로 새로운 픽셀 값을 생성합니다.
- 새로운 값을 해당 픽셀에 다시 설정합니다.
R, G, B 값이 모두 동일해지면 사람 눈에는 회색조(명도 차이만 존재하는 흑백)로 보이게 되는 원리입니다.
예제 코드
아래 예제는 D:\Images\car.jpg 파일을 읽어 모든 픽셀을 순회하며 그레이스케일로 변환한 후, grey_image.jpg라는 이름으로 저장하는 전체 과정을 보여줍니다.
import java.io.File;
import java.io.IOException;
import java.awt.Color;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Color2Grey {
public static void main(String args[])throws IOException {
// 이미지 파일 읽기
File file= new File("D:\\Images\\car.jpg");
BufferedImage img = ImageIO.read(file);
for (int y = 0; y < img.getHeight(); y++) {
for (int x = 0; x < img.getWidth(); x++) {
// 픽셀 값 가져오기
int pixel = img.getRGB(x,y);
// 픽셀 값으로 Color 객체 생성
Color color = new Color(pixel, true);
// R, G, B 값 추출
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
// R, G, B 값의 평균 계산
int average = (red+green+blue)/3;
// 새로운 Color 객체 생성
color = new Color(average, average, average);
// 변경된 색상을 이미지에 적용
img.setRGB(x, y, color.getRGB());
}
}
// 수정된 이미지 저장
file = new File("D:\\Images\\grey_image.jpg");
ImageIO.write(img, "jpg", file);
System.out.println("Done...");
}
}코드 설명
1. 이미지 로드: ImageIO.read() 메서드로 지정된 경로의 이미지 파일을 BufferedImage 객체로 불러옵니다.
2. 픽셀 순회: 이중 for문을 사용해 이미지의 가로(x)와 세로(y) 방향으로 모든 픽셀을 하나씩 방문합니다.
3. 색상 분리 및 평균 계산: getRGB()로 얻은 정수형 픽셀 값을 Color 객체로 감싸면 개별 R, G, B 값을 손쉽게 꺼낼 수 있습니다. 세 값을 더해 3으로 나누면 해당 픽셀의 밝기 평균이 나옵니다.
4. 새 색상 적용 및 저장: 평균값으로 만든 새 Color 객체를 setRGB()로 다시 할당하고, 최종적으로 ImageIO.write()를 통해 JPG 형식으로 저장합니다.
입력 이미지

출력 결과

참고 사항
단순 산술 평균 방식은 구현이 간단하지만, 인간의 시각은 녹색에 더 민감하기 때문에 실제로는 가중치를 적용한 루마(luma) 공식(예: 0.299×R + 0.587×G + 0.114×B)을 사용하면 더 자연스러운 흑백 이미지를 얻을 수 있습니다. 또한 픽셀 단위 반복 작업이 많은 대용량 이미지라면 성능을 위해 getRGB()/setRGB() 대신 버퍼 배열 단위 처리를 고려하는 것도 좋습니다.