Programming/Qt

[도서 실습] Qt 5 and OpenCV 4 Computer Vision ImageEditor (QPixmap, QImage, mat에 관해서)

변화의 물결1 2024. 2. 12. 00:59

 

 

안녕하세요.

 

 이전 내용에서 이미지가 흐려지는 기능(Blur) 사용해 보았는데, 소스상에서 OpenCV를 사용하기 위해서 이미지 변환을 하였습니다.

 QPixmap에서 QImage로 QImage에서 Mat으로 변환하였고, 다시 역으로 해서 이미지로 만들었습니다. 이 부분에 대해서 조금 더 설명하는 내용입니다. 내용이 조금 지루할 수 있으니 참고 삼아 읽어보시면 좋을 것 같습니다.


 

1. QPixmap

 

  QPixmap은 Qt 라이브러리에서 제공하는 클래스이며 화면에 이미지를 표시해야 할 때 사용하기 위한 것입니다. 이미지를 인스턴스로 읽고 그 인스턴스를 QGraphicsSence에 추가하여 표시합니다.

 QPixmap의 인스턴스를 만드는 방법에는 여러 가지가 있습니다.

 이전 섹션에서 했던 것처럼 이미지 파일의 경로를 사용하여 변수 생성 시 인스턴스화할 수 있습니다.

 

QPixmap map("/path/to/image.png");

 

 또는 새로운 QPixmap을 인스턴스화한 다음 나중에 이미지 데이터를 불러올 수 있습니다.

 

 QPixmap map;
 map.load("/Path/to/image.png");

 

 이미지가 들어 있는 QPixmap 인스턴스가 있는 경우, 다른 이름으로 저장 작업을 했던 것처럼 save 함수를 호출해서 파일에 이미지를 저장할 수 있습니다. 

 

 map.save("/path/to/output. png");

 

마지막으로 QPixmap 클래스의 toImage 함수를 호출하여 QImage 형식으로 변환할 수 있습니다.

 

 

2. QImage

 

  QPixmap은 주로 Qt에서 이미지를 표시하는 데 사용되지만 QImage는 I/O와 직접 픽셀 액세스와 조작을 위해 설계 및 최적화되었습니다.

 이 클래스를 사용하면 크기, 알파 채널이 있는지 여부, 회색조 이미지인지 여부 및 그 안의 픽셀 색상과 같은 이미지에 대한 정보를 얻을 수 있습니다.

 그러나 Qt 라이브러리는 이미지 처리 전용 라이브러리가 아니므로 여러 가지 이미지 처리 요구 사항을 만족시킬 수 없습니다. 따라서 QImage 객체를 Mat 객체로 변환한 후 OpenCV를 사용하여 이미지 처리를 할 수 있습니다.

 

 QImage, QPixmap 및 Mat 사이를 어떻게 변환할 수 있는가? 질문해 볼 수 있습니다.  이전 내용에서 QPixmap을 QImage로 변환하는 방법에 대해 살펴보았습니다. 다시 QImage을 QPixmap로 변환하는 방법을 보면 QImage 객체를 인수로 사용하여 QPixmap 클래스의 fromImage 정적 메서드를 호출하기만 하면 됩니다. 

 

QPixmap pixmap = QPixmap::fromImage(image);

 

  QImage의 세부 사항에 관심이 있는 경우 https://doc.qt.io/qt-5/qimage.html에서 참고할 수 있습니다. 다음 내용에서는 QImage를 Mat로 또는 그 반대로 변환하는 방법에 대해 보겠습니다.

 

 

3. Mat

 

 Mat 클래스는 OpenCV 라이브러리에서 가장 중요한 클래스 중 하나이고 Mat은 Matrix의 약자입니다. 컴퓨터 비전의 영역에서 모든 이미지는 너비, 높이, 채널 수 및 깊이를 가진 행렬입니다.

  따라서 OpenCV는 Mat 클래스를 사용하여 이미지를 나타냅니다. 사실, Mat 클래스는 주어진 데이터 유형으로 데이터의 단일 또는 다중 채널을 저장하는 데 사용할 수 있는 n차원 배열이며 다양한 방법으로 생성, 수정 또는 조작할 수 있는 많은 속성과 메서드가 포함되어 있습니다.

 

 Mat 클래스에는 많은 생성자가 있습니다. 예를 들어 너비(열)가 800이고 높이(행)가 600이고 8비트 unsigned int 값을 포함하는 세 개의 채널이 있는 인스턴스를 다음과 같이 만들 수 있습니다. 

 

 Mat mat(600, 800 , CV_8UC3);

 

 이 생성자의 세 번째 인수는 해당 행렬의 유형입니다. OpenCV는 사용할 수 있는 많은 값을 미리 정의합니다. 이러한 미리 정의된 값은 이름에 패턴이 있으므로 이름을 보고 행렬의 유형을 알 수 있거나 행렬의 특성이 결정될 때 사용해야 하는 이름을 추측할 수 있습니다.

 

 이 패턴을 CV_<깊이><유형> C <채널>이라고 합니다.

   - <깊이>는 픽셀의 각 요소를 저장하는 데 사용되는 비트 수를 나타내는 8, 16, 32 또는 64로 바꿀 수 있습니다.

   - <유형> 부호 없는 정수, 부호 있는 정수 및 부동 소수점 값에 대해 각각 U, S 또는 F로 바꿀 수 있습니다.

   - <channel>은 채널 수여야 합니다.

 

 작성한 코드에서 "CV_8UC3"은 선언된 이미지의 깊이가 8이고 픽셀의 각 요소가 8비트 unsigned int에 저장되며 3개의 채널을 의미합니다. 즉, 각 픽셀에는 3개의 요소가 있고 CV_8UC3은 24비트(깊이 * 채널)를 차지합니다.

 이미지를 구성할 때 일부 데이터로 이미지를 채울 수도 있습니다. 예를 들어 다음과 같이 일정한 색상으로 채울 수 있습니다.

 

      int R = 40, G = 50, B = 60;
      Mat mat(600, 800, CV_8UC3, Scalar(B, G, R));

 

 이전 예제에서 만든 것과 동일한 이미지를 만들었지만 네 번째 인수로 지정된 RGB(40, 50, 60) 상수 색상으로 채웠습니다.

 OpenCV의 기본 색상 순서는 RGB가 아니라 BGR이며 이는 B와 R값이 바뀌었음을 의미합니다. 따라서 코드에서 상수 색상을 Scalar(R, G, B) 대신 Scalar(B, G, R)로 표현합니다. 이것은 OpenCV를 사용하여 이미지를 읽는 경우 중요하지만 색상에 대해 다른 순서를 사용하는 다른 라이브러리를 사용하여 이미지를 조작하거나 그 반대의 경우도 마찬가지입니다. 특히 이미지 조작을 각 채널을 처리할 때 그렇습니다.

 

 현재 프로그램 소스에서도 Qt로 이미지를 로드하고 OpenCV Mat 데이터 구조로 변환한 다음 처리하고 다시 QImage로 변환합니다. 그러나 보시다시피 이미지를 흐리게 처리하는 동안 색상 순서에 따라 빨강 및 파랑 채널을 바꾸지 않았습니다.

  블러(Blur) 기능이 채널에서 대칭적으로 작동하기 때문입니다. 채널 간에 간섭이 없으므로 이 상황에서 색상 순서는 중요하지 않습니다. 다음을 수행하면 채널 교환을 생략할 수 있습니다.

 

  - QImage를 Mat로 변환한 다음 Mat를 처리하고 다시 QImage로 변환

  - Mat에서 수행하는 처리 기간 동안의 모든 조작은 채널에서 대칭입니다. 즉, 채널 간 간섭이 없습니다.

  - 처리 기간 동안 이미지를 표시하지 않습니다. QImage로 다시 변환된 후에만 표시합니다.

 

 이것은 우리가 나중에 작성할 대부분의 플러그인에 적용될 것입니다. 그러나 어떤 상황에서는 이것을 단순히 무시할 수 없습니다. 예를 들어 OpenCV를 사용하여 이미지를 읽고 QImage의 인스턴스로 변환한 다음 Qt에 표시하면 다음 코드는 빨간색과 파란색 채널이 바뀐 이미지를 표시합니다. 

      cv::Mat mat = cv::imread("/path/to/an/image.png");
      QImage image(
          mat.data,
          mat.cols,
          mat.rows,
          mat.step,
          QImage::Format_RGB888
      );

 

 

 QImage로 변환하기 전에 R과 B 채널을 바꿔야 합니다.

 

      cv::Mat mat = cv::imread("/path/to/an/image.png");
      cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);

      QImage image(
          mat.data,
          mat.cols,
          mat.rows,
          mat.step,
          QImage::Format_RGB888
      );

   

 OpenCV를 사용하여 색상 채널을 대칭적으로 처리하지 않는 프로세스를 사용하는 경우 해당 작업을 수행하기 전에 색상 순서가 BGR인지 확인해야 합니다.

 만들 애플리케이션에서는 주어진 QImage 객체의 동일한 이미지를 보유하는 Mat 객체를 생성해야 합니다. 이 작업을 수행한 방법을 다시 살펴보겠습니다. 

     // image is the give QImage object
      cv::Mat mat = cv::Mat(
          image.height(),
          image.width(),
          CV_8UC3,
          image.bits(),
          image.bytesPerLine()
      );

 

 이전에 이미 이야기한 처음 세 개의 인수 외에도 QImage 객체의 비트 메서드가 반환한 데이터 포인터를 네 번째 인수로 넣어줍니다. 또한 OpenCV가 이미지 패딩 바이트를 처리하는 방법과 효율적인 방법으로 메모리에 저장하는 방법을 알려주기 위한 이미지 행당 바이트 수를 인수로 전달할 수도 있습니다.

 

 Mat 클래스의 생성자는 많이 있고, 훨씬 더 높은 차원의 Mat 개체를 만들 수도 있습니다.

 https://docs.opencv.org/4.0.0/d3/d63/classcv_1_1Mat.html의 문서를 참조하여 생성자의 전체 목록을 얻을 수 있습니다.

 

  이제 Qt와 OpenCV 간에 이미지 개체를 변환하는 방법에 대한 지식을 얻었으므로 다음 내용에서 OpenCV를 사용하여 이미지를 편집하는 방법 알아보도록 하겠습니다.

 

감사합니다.

 

 

<참고자료>

1. [BOOK] Qt-5-and-OpenCV-4-Computer-Vision-Projects

 

반응형