이미지 편집 응용 프로그램의 핵심 기술은 주요 색상을 결정하는 것입니다. 이러한 주요 색상을 식별하는 것은 선택한 도구의 효과와 편집 결과를 정확하게 반영하는 이미지 팔레트를 만드는 데 필수적입니다. 이 기사에서는 제한된 색상 공간 내에서 이미지의 팔레트를 결정하고, 이미지의 주요 색상을 식별하고, 팔레트와 주요 색상을 구별하는 방법을 자세히 살펴보겠습니다. 팔레트 이미지 팔레트는 일반적으로 원본 이미지에 있는 모든 색상에 대한 참조입니다. 본질적으로 색상 자극 신호 값의 수치 척도를 기반으로 전체 색상 범위를 캡처합니다. 특정 수준의 정확도로 신호를 모델링하는 데 동의하면 이러한 신호 값의 범위가 사용 가능한 색상 팔레트를 나타냅니다. 이미지의 각각의 고유한 표현과 이 공간에 대한 이미지 색상의 매핑은 이미지의 하위 집합이 됩니다. 디지털 신호 처리(이미지도 신호임)에서 우리는 종종 이산적 표현을 통해 다양한 양을 이해합니다. 따라서 이미지 팔레트는 개별 맵으로 표시되는 이미지의 모든 색상의 하위 집합으로 볼 수 있습니다. 각 색상은 색인화되어 색상 공간 중 하나에 특정 값을 할당할 수 있습니다. 우리의 목적을 위해 RGB 색상 모델을 사용하겠습니다. 이러한 방식으로 이미지 팔레트를 제시하는 데 있어서 중요한 과제는 인간의 가시성이 광범위하다는 것입니다. 전체 이미지 팔레트를 수동으로 분석할 가능성은 거의 없으며 모든 원본 색상으로 표현된 이미지를 저장하는 것은 의미가 없는 경우가 많습니다. 대신, 우리는 그 수를 합리적인 한도로 줄입니다. 색상 양자화라고 하는 이 프로세스에는 이미지에서 표현할 수 있는 전체 하위 집합의 색상 수를 더 작은 색상으로 줄이는 작업이 포함됩니다. 예를 들어, 14비트 원시 색상 데이터는 8비트 JPEG 또는 8비트 PNG로 변환된 16비트 TIFF로 표현될 수 있습니다. 이미지의 기본 색상을 결정하는 간단한 솔루션은 8개 또는 3개 색상과 같이 매우 제한된 세트로 색상 양자화 프로세스를 구상하는 것입니다. 이를 통해 이미지 내의 기본 색상에 대한 통찰력을 얻을 수 있어 더욱 눈에 띄고 기억에 남을 수 있습니다. 이상적으로는 또는 이 설명한 원칙에 따라 최종 이미지의 색상이 조화로워야 합니다. Johannes Itten Matyushin 이미지의 주요 색상을 식별함으로써 이미지의 "조화"를 시각화할 수 있는 도구를 만들 수 있습니다. 이는 필터링 후 이미지의 조화, 즉 "합성 조화"에도 적용될 수 있습니다. 아래에서는 그러한 도구를 개발해 보겠습니다. 메디안 컷 이미지 팔레트를 압축하기 위해 널리 사용되는 고품질 알고리즘은 Median Cut 알고리즘입니다. 비록 일부 수정은 있었지만 JPEG와 같은 대부분의 주요 손실 이미지 압축 알고리즘에 통합되었습니다. Median Cut 알고리즘의 기본 원리는 이미지의 3차 히스토그램을 중앙값으로 순차적으로 나누는 것입니다. 각 결과 하위 큐브에는 거의 동일한 수의 빈 또는 동일한 색상의 픽셀이 포함됩니다. 분할 프로세스가 미리 결정된 하위 큐브 수에 도달하면 각 큐브의 평균 색상을 계산하고 이를 원본 이미지의 해당 색상에 매핑합니다. 이 프로세스를 통해 원본 이미지의 색상 수를 효과적으로 줄여 이미지를 더 작은 파일 크기로 압축할 수 있습니다. 언뜻 보면 이 알고리즘의 초기 부분이 이미지의 기본 색상 또는 주요 색상을 식별하는 문제를 해결할 수 있는 것처럼 보일 수 있습니다. 그러나 이미지를 분할하는 색상 수가 너무 적고, 소수의 색상만 분석하는 경우 잠재적인 문제가 발생합니다. 색상이 과도하게 평균화되었기 때문에 이미지에 실제로 존재하지 않는 색상을 식별하게 될 수도 있습니다. 각 큐브에서 최대 빈을 가진 색상을 선택하고 이를 주요 색상으로 표시할 수 있지만 그러면 더 이상 팔레트를 구성하지 않습니다. 따라서 우리는 이미지 팔레트의 하기 위해 이 접근 방식을 예약할 것이며, 이는 "조화로운 유틸리티" 측면에서 이미지를 시각적으로 분석하기 위한 도구로도 사용할 수 있습니다. 주요 색상을 식별하기 위해 통계 분석을 사용합니다. 동일한 3차 히스토그램에서 로컬 최대값을 검색합니다. 압축된 버전을 결정 로컬 최대값 로컬 최대값을 식별하기 위해 특정 코드를 구현합니다. 숙련된 예술가이기도 한 저자는 알고리즘에 대해 탁월한 설명을 제공합니다. 기본적으로 먼저 이미지 통계를 Median Cut 알고리즘에 사용된 것과 동일한 3차원 RGB 히스토그램으로 수집합니다. 3차 히스토그램의 각 셀에는 색상 저장소가 포함되며 셀에 포함된 각 색상의 모든 값의 합계가 포함됩니다. 32x32x32(원래 30x30x30)의 해상도로 제한된 히스토그램 크기를 고려하면 합계를 누적하면 평균 셀 색상 계산이 단순화됩니다. 그런 다음 전체 공간을 철저하게 탐색하고 이를 이웃 셀과 비교하여 로컬 최대값을 검색합니다. 그런 다음 반복적으로 로컬 최대값을 필요한 양으로 줄여 비슷한 색상을 더 적은 가중치로 삭제합니다. 나머지 모든 로컬 최대값에 대해 목록에 포함된 모든 값의 평균 색상을 계산합니다. 로컬 최대값의 색상 밀도가 더 높고 픽셀 값 간의 차이가 중앙 컷의 큐브보다 작기 때문에 색상은 기본 이미지에 있는 색상과 더 유사하고 주요 색상을 더 정확하게 나타냅니다. 이는 획득과 로컬 최대값 또는 검색이라는 두 모델 간의 주요 차이점을 보여줍니다. 기본 "압축된 팔레트" 이미지를 매핑함으로써 비록 크게 잘린 형태이기는 하지만 새로운 이미지를 생성합니다. "압축된 팔레트" 주요 색상 기본 이미지와 동일한 색상 균형을 유지하는 반면, 주요 색상은 . 적절한 색상 균형으로는 새로운 것으로 변형될 수 없습니다. 이미지에 주로 나타나는 색상의 구성만을 설명합니다 구현 이 작업을 예로 들어 사용하여 이미지 분석 및 조작을 위해 즉시 사용 가능한 애플리케이션을 개발하는 것이 얼마나 간단한지 보여드리겠습니다. 다른 엔진에는 없는 기능부터 시작하겠습니다. IMProcessing Framework를 예를 들어, 프레임워크에는 기존 CLUT가 포함된 Adobe 파일을 읽을 수 있는 기능이 있으며 이미지에서 3차원 입방형 히스토그램을 실시간으로 추출할 수 있습니다. .cube 이를 활용하여 우리는 다음을 수행할 수 있는 애플리케이션을 만드는 것을 목표로 합니다. JFIF(jpeg) 형식으로 파일을 업로드하세요. 원본 이미지를 "정규화"합니다. "노멀라이저"의 강도를 제어합니다. Adobe .cube 파일의 임의 LUT를 처리에 통합합니다. CLUT 영향력의 강도를 관리합니다. 영상의 선형 히스토그램을 표시합니다. "압축된 팔레트"와 이미지의 주요 색상은 물론 삼중선(r,g,b) 형태의 숫자 표현도 표시합니다. 우리 건설의 최종 제품은 다음과 같은 대화형 장난감과 유사합니다. 여기에서는 이미지의 단순한 "정규화"가 최종 팔레트의 다양성에 긍정적인 영향을 미치고 주요 색상을 보다 다양하고 조화로운 세트로 재분배하는 방법이 분명합니다. "노멀라이저" 우리는 기존의 두 필터로부터 필터를 조립할 것입니다: - 이미지 히스토그램을 지정된 경계까지 늘릴 수 있습니다. IMPContrastFilter - 평균 색상 검색과 이미지의 잘못된 톤 수정을 기반으로 자동 화이트 밸런스 수정을 수행합니다. 이는 본질적으로 IMPAutoWBFilter 에서 빌린 아이디어를 약간 수정한 것입니다. Andrey Zhuravlev의 블로그 import IMProcessing /// Image filter public class IMPTestFilter:IMPFilter { /// We will use a contrast control filter through histogram stretching var contrastFilter:IMPContrastFilter! /// Auto white balance filter var awbFilter:IMPAutoWBFilter! /// Image Linear Histogram Analyzer var sourceAnalyzer:IMPHistogramAnalyzer! /// Solver of the histogram analyzer for calculating the lightness boundaries of the image let rangeSolver = IMPHistogramRangeSolver() public required init(context: IMPContext) { super.init(context: context) // Initialize filters in context contrastFilter = IMPContrastFilter(context: context) awbFilter = IMPAutoWBFilter(context: context) // Add filters to the stack addFilter(contrastFilter) addFilter(awbFilter) // Initialize the histogram analyzer sourceAnalyzer = IMPHistogramAnalyzer(context: self.context) // Add a light boundary search solver to the analyzer sourceAnalyzer.addSolver(rangeSolver) // Add an observing handler to the filter for // to pass the current image frame to the analyzer addSourceObserver { (source) - Void in self.sourceAnalyzer.source = source } // Add an observing handler for updating analysis calculations to the analyzer sourceAnalyzer.addUpdateObserver({ (histogram) - Void in // set the lightness boundaries in the contrast filter each time the image changes self.contrastFilter.adjustment.minimum = self.rangeSolver.minimum self.contrastFilter.adjustment.maximum = self.rangeSolver.maximum }) } } 팔레트 솔버 IMProcessing 프레임워크는 고유한 계산 구성으로 인해 많은 유사한 플랫폼보다 돋보입니다. 기본적으로 필터를 처리하지 않는 전용 필터 그룹을 사용합니다. 이러한 클래스의 개체는 동종 및 공간 영역의 이미지를 수정하지 않습니다. 대신 특정 문제를 해결하기 위해 특수 확장기에서 분석용 메트릭에 대한 특정 계산 및 표현을 수행합니다. 예를 들어 IMPHistogramAnalyzer 클래스의 객체는 이미지의 평균 색상, 조명 범위, 구역 분할 등을 계산하는 여러 솔버를 동시에 추가할 수 있습니다. 우리는 솔버를 사용하여 IMPHistogramCubeAnalyzer의 분석을 확장하여 팔레트와 주요 색상 목록을 계산합니다. 그러면 계산 결과가 업데이트된 NSTableView에 표시됩니다. import IMProcessing /// Types of distribution of image color accents /// /// - palette: palette for quantizing image colors. /// calculated using the median-cut transformation scheme: /// http://www.leptonica.com/papers/mediancut.pdf /// - dominants: calculation of dominant colors of an image by searching for local maxima /// color distribution density functions: /// https://github.com/pixelogik/ColorCube /// public enum IMPPaletteType{ case palette case dominants } /// Solver of the cubic color histogram analyzer IMPHistogramCubeAnalyzer public class IMPPaletteSolver: IMPHistogramCubeSolver { /// Maximum number of palette colors for analysis public var maxColors = Int(8) /// List of found colors public var colors = [IMPColor]() /// Palette type public var type = IMPPaletteType.dominants /// Solver handler handler /// - parameter analyzer: link to the analyzer /// - parameter histogram: cubic histogram of the image /// - parameter imageSize: image size public func analizerDidUpdate(analizer: IMPHistogramCubeAnalyzer, histogram: IMPHistogramCube, imageSize: CGSize) { var p = [float3]() if type == .palette{ p = histogram.cube.palette(count: maxColors) } else if type == .dominants { p = histogram.cube.dominantColors(count: maxColors) } colors.removeAll() for c in p { colors.append(IMPColor(color: float4(rgb: c, a: 1))) } } } View Controller의 모든 구성요소를 조립합니다. 컨트롤러에는 라고 하는 기본 애플리케이션 필터, IMPLutFilter라는 CLUT 필터, "일반" 히스토그램을 표시하기 위해 즉시 사용할 수 있는 IMPHistogramView, 우리가 연결할 입방형 히스토그램 분석기인 IMPHistogramCubeAnalyzer가 필요합니다. 우리의 솔버인 IMPPaletteSolver입니다. IMPTestFilter 마지막으로 이미지를 표시하기 위한 기본 창으로 IMPImageView를 사용하고 공통 IMPContext는 프레임워크의 모든 생성자가 사용하는 주요 클래스입니다. class ViewController: NSViewController { // // Processing context // let context = IMPContext() // // Window for presenting the loaded image // var imageView:IMPImageView! var pannelScrollView = NSScrollView() // // Window for displaying the image histogram // var histogramView:IMPHistogramView! // // NSTableView - views of a list of colors from the palette // var paletteView:IMPPaletteListView! // // Main filter // var filter:IMPTestFilter! // // CLUT filter from Adobe Cube files // var lutFilter:IMPLutFilter? // // Analyzer of a cubic histogram of an image in RGB space // var histograCube:IMPHistogramCubeAnalyzer! // // Our solver for finding colors // var paletteSolver = IMPPaletteSolver() var paletteTypeChooser:NSSegmentedControl! override func viewDidLoad() { super.viewDidLoad() configurePannel() // // Initialize the objects we need // filter = IMPTestFilter(context: context) histograCube = IMPHistogramCubeAnalyzer(context: context) histograCube.addSolver(paletteSolver) imageView = IMPImageView(context: context, frame: view.bounds) imageView.filter = filter imageView.backgroundColor = IMPColor(color: IMPPrefs.colors.background) // // Add another handler to monitor the original image // (another one was added in the main filter IMPTestFilter) // filter.addSourceObserver { (source) -> Void in // // to minimize calculations, the analyzer will compress the image to 1000px on the wide side // if let size = source.texture?.size { let scale = 1000/max(size.width,size.height) self.histograCube.downScaleFactor = scale.float } } // Add an observer to the filter to process the filtering results // filter.addDestinationObserver { (destination) -> Void in // pass the image to the histogram indicator self.histogramView.source = destination // pass the result to the cubic histogram analyzer self.histograCube.source = destination } // // The results of updating the analyzer calculation are displayed in the color list window // histograCube.addUpdateObserver { (histogram) -> Void in self.asyncChanges({ () -> Void in self.paletteView.colorList = self.paletteSolver.colors }) } view.addSubview(imageView) .... IMPDocument.sharedInstance.addDocumentObserver { (file, type) -> Void in if type == .Image { do{ // // Load the file and associate it with the filter source // self.imageView.source = try IMPImageProvider(context: self.imageView.context, file: file) self.asyncChanges({ () -> Void in self.zoomFit() }) } catch let error as NSError { self.asyncChanges({ () -> Void in let alert = NSAlert(error: error) alert.runModal() }) } } else if type == .LUT { do { // // Initialize the CLUT descriptor // var description = IMPImageProvider.LutDescription() // // Load CLUT // let lutProvider = try IMPImageProvider(context: self.context, cubeFile: file, description: &description) if let lut = self.lutFilter{ // // If a CLUT filter has been added, update its LUT table from the file with the received descriptor // lut.update(lutProvider, description:description) } else{ // // Create a new LUT filter // self.lutFilter = IMPLutFilter(context: self.context, lut: lutProvider, description: description) } // // Add a LUT filter, if this filter has already been added nothing happens // self.filter.addFilter(self.lutFilter!) } catch let error as NSError { self.asyncChanges({ () -> Void in let alert = NSAlert(error: error) alert.runModal() }) } } } .... 보시다시피, 사진 처리는 점점 더 단순해지고 일반 사용자가 더 쉽게 접근할 수 있게 되어 거의 모든 사람이 이 프로세스를 마스터할 수 있게 되었습니다. 전체 프로젝트는 프로젝트 인 에서 다운로드, 조립 및 테스트할 수 있습니다. 적절한 어셈블리를 위해서는 JPEG 파일(JFIF) 작업을 위한 대형 라이브러리 로컬에 설치해야 합니다. ImageMetalling 저장소 ImageMetalling-08 인 libjpeg-turbo를 현재 이는 이 형식에 대한 최상의 지원 구현입니다.