画像編集アプリケーションにおける重要なテクニックは、主要な色を決定することです。これらの主要な色の特定は、画像のパレットを作成するために不可欠であり、その結果、選択したツールの効果と編集の結果が正確に反映されます。 この記事では、制限された色空間内で画像のパレットを決定する方法、画像のドミナントカラーを特定する方法、およびパレットとドミナントカラーを区別する方法について詳しく説明します。 パレット 画像パレットは通常、元の画像に存在するすべての色への参照です。本質的には、色刺激信号値の数値スケールに基づいて、色合いの全範囲をキャプチャします。 一定レベルの精度で信号をモデル化することに同意した場合、これらの信号値の範囲が利用可能なカラー パレットを表します。 画像のそれぞれの一意な表現と、この空間への画像の色のマッピングは、そのサブセットになります。デジタル信号処理 (画像も信号です) では、離散表現を通じてさまざまな量を理解することがよくあります。 したがって、画像パレットは、離散マップで表される画像内のすべての色のサブセットとして見ることができます。各色にインデックスを付け、色空間の 1 つで特定の値を割り当てることができます。この目的のために、RGB カラー モデルを使用します。 この方法で画像パレットを表示する際の大きな課題は、人間の可視範囲が広大であることです。画像パレット全体を手動で分析できる可能性は低く、元のすべての色で表される画像を保存することは多くの場合意味がありません。代わりに、その数を合理的な制限まで削減します。 カラー量子化として知られるこのプロセスには、画像内で表現できる完全なサブセットから色の数を減らすことが含まれます。たとえば、14 ビットの生のカラー データは、8 ビット PNG に変換された 8 ビット JPEG または 16 ビット TIFF で表現される場合があります。 画像の原色を決定する簡単な解決策は、8 色または 3 色などの非常に限られたセットに色を量子化するプロセスを想像することです。これにより、画像内の原色についての洞察が得られ、より目立ち、記憶に残るものになります。 理想的には、最終画像の色は、 または によって概説された原則に従って調和している必要があります。 ヨハネス イッテン マチューシン 画像の主要な色を特定することで、画像の「調和」を視覚化できるツールを作成できます。これは、フィルタリング後の画像の調和、つまり「合成調和」にも当てはまります。 以下では、そのようなツールの開発を試みます。 メディアンカット 画像のパレットを圧縮するために広く使用されている高品質のアルゴリズムは、メディアン カット アルゴリズムです。これは、いくつかの変更はありますが、JPEG などのほとんどの主要な非可逆画像圧縮アルゴリズムに組み込まれています。 メディアン カット アルゴリズムの基本原理は、画像の 3 次ヒストグラムをメディアンに順次分割することです。結果として得られる各サブキューブには、ほぼ同数のビン、つまり同じ色のピクセルが含まれます。 分割プロセスが所定の数のサブキューブに達すると、各立方体の平均色を計算し、それを元の画像の対応する色にマッピングします。このプロセスにより、元の画像の色数が効果的に削減され、画像をより小さいファイル サイズに圧縮できるようになります。 一見すると、このアルゴリズムの最初の部分で、画像の原色または支配的な色を識別するという問題を解決できるように思えるかもしれません。ただし、画像を分割する色の数が少なすぎて、少数の色しか分析していない場合には、潜在的な問題が発生します。 色が過度に平均化されるため、実際には画像に存在しない色を識別してしまう可能性があります。各立方体から最大のビンを持つ色を選択し、それを支配的な色としてラベル付けすることはできますが、そうすると、それはパレットを構成しなくなります。 したがって、このアプローチは、画像のパレットの するために保留しておきます。これは、「調和のとれたユーティリティ」という観点から画像を視覚的に分析するためのツールとしても使用できます。支配的な色を特定するために、同じ 3 次ヒストグラム内の極大値を検索するという統計分析を使用します。 圧縮バージョンを 決定 ローカルマキシマ 極大値を特定するために、特定のコードを実装します。熟練したアーティストでもある著者は、アルゴリズムについて優れた説明を提供しています。基本的に、まず画像統計を収集して、メディアン カット アルゴリズムで使用されるのと同じ 3 次元 RGB ヒストグラムを作成します。 三次ヒストグラムの各セルにはカラー ビンと、セルに含まれる各色のすべての値の合計が含まれます。ヒストグラムの寸法が解像度 32x32x32 (元は 30x30x30) に制限されている場合、合計を累積すると、平均セル色の計算が簡素化されます。 次に、空間全体を徹底的に探索し、隣接するセルと比較することで極大値を検索します。 これに続いて、局所最大値の数を必要な量まで繰り返し減らし、重みの少ない類似した色を破棄します。残りのすべての極大値について、リストに含まれるすべての値の平均色を計算します。 極大値の色密度が高く、ピクセル値間の差が中央値カットの立方体よりも小さいため、色はメイン画像に存在する色により近くなり、その主要な色がより正確に表現されます。 これにより、2 つのモデルの主な違いが明らかになります。 の取得と、極大値または の検索です。プライマリの「圧縮パレット」画像をマッピングすることにより、大幅に切り詰められた形ではありますが、 新しい画像を作成します。 「圧縮パレット」 支配的な色 メイン画像と同じカラー バランスを維持する 一方、ドミナントカラーは、 。適切なカラーバランスで新しいものに変えることはできません。 画像内に主に存在する 色の構成のみを記述します 実装 このタスクを例として使用して、 を使用して画像の分析と操作のためのすぐに使用できるアプリケーションを開発することがいかに簡単であるかを示します。他のエンジンにはない機能から始めます。 IMProcessing Framework たとえば、このフレームワークには、既存の CLUT を含む Adob ファイルを読み取る機能があり、画像からリアルタイムで 3 次元の 3 次元ヒストグラムを抽出できます。 e .cube これを利用して、次のことができるアプリケーションの作成を目指しています。 JFIF (jpeg) 形式でファイルをアップロードします。 元の画像を「正規化」します。 「ノーマライザー」の強度を制御します。 Adobe .cube ファイルからの任意の LUT を処理に組み込みます。 CLUT の影響の強さを管理します。 画像の線形ヒストグラムを表示します。 「圧縮パレット」と画像の主要な色、およびそれらの数値表現を 3 つ組 (r、g、b) の形式で表示します。 私たちの構築の最終製品は、次のインタラクティブなおもちゃのようになります。 ここでは、画像の単純な「正規化」が最終的なパレットの多様性にどのようにプラスの影響を与え、主要な色をより多様で調和のとれたセットに再配分するかが明らかです。 「ノーマライザー」 2 つの既存のものからフィルターを組み立てます。 - 画像ヒストグラムを指定された境界まで引き伸ばすことができます。 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))) } } } ビューコントローラー内のすべてのコンポーネントを組み立てます コントローラーには、 と呼ぶメイン アプリケーション フィルター、 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 現時点では、これがこの形式のサポートの最良の実装です。