程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> SqlServer數據庫 >> 關於SqlServer >> GDI+ 在Delphi程序的應用 -- 圖像飽和度調整

GDI+ 在Delphi程序的應用 -- 圖像飽和度調整

編輯:關於SqlServer
  圖像的飽和度調整有很多方法,最簡單的就是判斷每個象素的R、G、B值是否大於或小於128,大於加上調整值,小於則減去調整值;也可將象素RGB轉換為HSV或者HSL,然後調整其S部分,從而達到線性調整圖象飽和度的目的。這幾種方法我都測試過,效果均不太好,簡單的就不說了,利用HSV和HSL調整飽和度,其調節范圍很窄,飽和度沒達到,難看的色斑卻出現了。而Photoshop的飽和度調整調節范圍大多了,效果也好多了,請看下面25%飽和度調整時幾種方法的效果對比圖:

可以看出,都是25%的飽和度調整,Photoshop的調節幅度顯得小一些(平坦些),效果也好多了,而HSV和HSL均出現了色斑,某些顏色也嚴重失真,尤其是HSV方式。

        據網上和書上的介紹,Photoshop的是利用所謂HSB顏色模式實現色相/飽和度調節的,可是就是沒有看到其算法,我只得自己進行琢磨,首先發現Photoshop色相/飽和度命令中的明度調節好象是“獨立”的,也就是它不需要轉換為所謂的HSB模式,直接靠白色和黑色遮照層進行調節,具體原理和代碼可看我寫的《GDI+ 在Delphi程序的應用 -- 仿Photoshop的明度調整》一文。後來,卻又發現Photoshop的飽和度調節好象是“半獨立的”,什麼意思呢?就是說Photoshop的色相/飽和度的調整還是轉換為HSL顏色模式進行的,只是飽和度的增減調節卻是“獨立”於SHL模式的另外一套算法,如果不是需要HSL的S和L部分進行飽和度的上下限控制,它也和明度調整一樣,可以獨立進行!下面是我寫的C++算法(只是隨手寫的算法,不是真正的運行代碼):




inline void SwapRGB(int &a, int &b)
{
    a += b;
    b = a 

n style="COLOR: #000000">- b;
    a -= b;
}

    // 利用HSL模式求得顏色的S和L
    double rgbMax = R / 255;
    double rgbMin = G / 255;
    double rgbC = B / 255;
    if (rgbMax < rgbC)
        SwapRGB(rgbMax, rgbC);
    if (rgbMax < rgbMin)
        SwapRGB(rgbMax, rgbMin);
    if (rgbMin > rgbC)

        SwapRGB(rgbMin, rgbC);
    double delta = rgbMax - rgbMin;
    // 如果delta=0,S=0,所以不能調整飽和度
    if (delta == 0) return;
        
    double value = rgbMax + rgbMin;
    double S, L = value / 2;
    if (L < 0.5)
        S = delta / value;
    else<

/span>
        S = delta  / (2 - value);
    // 具體的飽和度調整,sValue為飽和度增減量
    // 如果增減量>0,飽和度呈級數增強,否則線性衰減
    if (sValue > 0)
    {
        // 如果增減量+S > 1,用S代替增減量,以控制飽和度的上限
        // 否則取增減量的補數
        sValue = sValue + S >= 1? S : 1 - sValue;
        // 求倒數 - 1,

實現級數增強
        sValue = 1 / sValue - 1;
    }
    // L在此作飽和度下限控制
    R = R + (R - L * 255) * sValue;
    G = G + (G - L * 255) * sValue;
    B = B + (B - L * 255

0000">) * sValue;

        從上面的算法代碼中可以看到,Photoshop的飽和度調整沒有像HSV和HSL的飽和度調整那樣,將S加上增減量重新計算,並將HSL轉換回RGB,而只是取得了顏色的S、L作為上下限控制,對原有的RGB進行了“補丁”式的調節。

        下面是根據以上算法寫的Delphi的BASM代碼和GDI+調用的飽和度調整過程:



// Value:飽和度調整量(-255 - +255,沒作范圍檢查)
procedure PSSaturation(Data: TBitmapData; Value: Integer);
var
  L, sc, sv, width, off: Integer;
asm
    push  &nbsp; esi
    push    edi
    push    ebx
    mov     esi, [eax + 16]
    mov     ecx, [eax]
    mov     width, ecx
    shl     ecx, 1
    add     ecx, [eax]
    mov     ebx, [eax + 8]
    sub     ebx, ecx
    mov     off, ebx
    mov     sv, edx
    mov     edi, 255
    mov     ecx, [eax +

LOR: #000000"> 4]
  @yLoop:
    push    ecx
    mov     ecx, width
  @xLoop:
    movzx   eax, [esi + 2]
    movzx   ebx, [esi + 1]
    movzx   edx, [esi]
    cmp     edx, ebx
    jge     @@1
    xchg    edx, ebx
  @@1:
    cmp     edx, eax
    jge     @@2
    xchg    edx, eax
  @@2:
    cmp     ebx, eax
    jle     @@3
    mov     ebx, eax
  @@3:
    mov     eax, edx
    sub     eax, ebx       // delta = varMax - varMin

r />    jnz     @@4
    add     esi, 3
    jmp     @@20           // if (delta == 0) continue
  @@4:
    add     edx, ebx
    mov     ebx, edx       // ebx = varMax + varMin
    shr     edx, 1
    mov     L, edx         // L = (varMax + varMin) / 2
    cmp     edx, 128
    jl      @@5
    neg     ebx            // if (L >= 128) ebx = 510 - ebx

r />    add     ebx, 510
  @@5:
    imul    eax, edi       // eax = S = delta * 255 / ebx
    cdq
    div     ebx

    mov     ebx, sv       // ebx = sv
    test    ebx, ebx      // if (ebx > 0)
    JS      @@10          // {
    add     ebx, eax
    cmp     ebx, edi      //   if (ebx + S >= 255) ebx = S
    jl      @@6
    mov     ebx,eax
    jmp     @@7
  @@6:
    mov     ebx, edi
    sub     ebx, sv       //   else ebx = 255 - ebx
  @@7:
    mov     eax, 65025    //   ebx = 65025 / ebx - 255
    cdq
    div     ebx
    sub     eax, 255
    mov     ebx, eax      // }
  @@10:
    mov     sc, ebx       // sc = ebx
    push    ecx
    mov     ecx, 3
  @vLoop:       &nbsp;         // for (ecx = 3; ecx > 0; ecx --)
    movzx   eax, [esi]    //   rgb = rgb + (rgb - L) * sc / 255
    mov     ebx, eax
    sub     eax, L
    imul    eax, sc
    cdq
    idiv    edi
    add     eax, ebx
    jns     @@11
    xor     eax, eax
    jmp     @@12
  @@11:
    cmp     eax, edi
    jle     @@12
    mov     eax, edi
  @@12:
    mov     [esi], al
    inc     esi
    loop    @vLoop
    pop     ecx
  @@20:
    dec     ecx
  &nbs p; jnz     @xLoop
    add     esi, off
    pop     ecx
    dec     ecx
    jnz     @yLoop
    pop     ebx
    pop     edi
    pop     esi
end;

procedure GdiPPSSaturation(Bmp: TGpBitmap; Value: Integer);
var
  Data: TBitmapData;
begin
  Data := Bmp.LockBits(GpRect(0, 0, Bmp.Width, Bmp.Height), [imRead, imWrite], pf24bppRGB);
  try
    PSSaturation(Data, Value);
  finally
    Bmp.UnlockBits(Data);
  end;
end;

        具體的測試代碼就不寫了,有興趣者可參考我的《GDI+ 在Delphi程序的應用 -- 線性調整圖像亮度》、《GDI+ 在Delphi程序的應用 -- 圖像卷積操作及高斯模糊》和《GDI+ 在Delphi程序的應用 -- 圖像亮度/對比度調整》等文章,寫出GDI+的TGpBitmap和Delphi的TBitmap的測試代碼,其運行結果與Photoshop完全一樣。  

  對於色相的調整,HSV、HSL和HSB都是相同的,不同的只是飽和度和亮度(明度)的調整,前天我已經寫了《GDI+ 在Delphi程序的應用 -- 仿Photoshop的明度調整》,加上這篇飽和度算法文章,是否意味Photoshop的HSB算法完全破解了呢?不然,Photoshop的飽和度和明度調整獨立使用時,確實是我說的那樣,與Photoshop效果完全一樣,但是放在一起進行調節就有區別了,這裡有個誰先誰後的時機問題,

和我前天寫的《GDI+ 在Delphi程序的應用 -- 圖像亮度/對比度調整》中對比度和亮度關系一樣,各自獨立使用沒問題,放在一起調整就麻煩,但是對比度和亮度的關系比較簡單,幾次測試就清楚了,而飽和度和明度的關系我試驗過多次,均與Photoshop有區別(只是有區別而以,其效果不比Photoshop的差多少),所以,要完全破解,還得試驗,如果有誰知道,請務必告知,本人在此先謝了。下面干脆把我用BCB6寫的試驗性代碼完整的貼在這,有興趣的朋友可以幫忙試驗,這個試驗代碼寫的很零亂,運行也不快,先調整飽和度,再調整明度,其他方式沒成功,所以沒保存結果。



// rgbhsb.h

#ifndef RgbHsbH
#define RgbHsbH

#include <Windows.h>
#include <algorithm>
using std::min;
using std::max;
#include <gdiplus.h>
using namespace Gdiplus;

void SetRgbHsb(unsigned char &R, unsigned char &G, unsigned char &B,


               int hValue, int sValue, int bValue);
void GdipHSBAdjustment(Bitmap *Bmp, int hValue, int sValue, int bValue);

//---------------------------------------------------------------------------
#endif

// rgbhsb.cpp

#pragma hdrstop

#include "RgbHsb.h"

//---------------------------------------------------------------------------

inline void SwapRGB(int &a, int &00000">b)
{
    a += b;
    b = a - b;
    a -= b;
}

inline void CheckRGB(int &Value)
{
    if (Value < 0) Value = 0;
    else if (Value > 255) Value = 255;
}

inline void AssignRGB(unsigned char &R, unsigned char &G, unsigned char

000000"> &B, int rv, int gv, int bv)
{
    R = rv;
    G = gv;
    B = bv;
}

void SetRgbHsb(unsigned char &R, unsigned char &G, unsigned char &B, int hValue, int sValue, int bValue)
{
    int rgbMax = R;
    int rgbMin = G;
    int rgbC = B;
   &nbsp;if (rgbMax < rgbC)
        SwapRGB(rgbMax, rgbC);
    if (rgbMax < rgbMin)
        SwapRGB(rgbMax, rgbMin);
    if (rgbMin > rgbC)
        SwapRGB(rgbMin, rgbC);
    int value = rgbMax + rgbMin;
    int L = (value + 1) >> 1;
    int H, S;
    int delta = rgbMax - rgbMin;
    if (!delta)

       H = S = 0;
    else
    {
        if (L < 128)
            S = delta * 255 / value;
        else
            S = delta * 255 / (510 - value);
        if (rgbMax == R)
            H = (G -

n> B) * 60 / delta;
        else if (rgbMax == G)
            H = (B - R) * 60 / delta + 120;
        else
        &nbsp;   H = (R - G) * 60 / delta + 240;
        if (H < 

COLOR: #000000">0) H += 360;
        if (hValue)
        {
            H += hValue;
            if (H < 0) H += 360;
            else if (H > 360) H -= 360;
            int m = H % 60;
            H /= 60

0">;
            if (H & 1) m = 60 - m;
            rgbC = (m * 255 + 30) / 60;
            rgbC = rgbC - (rgbC - 128) * (255 - S) / 255;
            int Lum =

> L - 128;
            if (Lum > 0)
                rgbC = rgbC + ((255 - rgbC) * Lum + 64) / 128;
            else if (Lum < 0)
                rgbC = rgbC + rgbC * Lum / 128;
 

       }
        else H /= 60;
        if (sValue)
        {
            if (sValue > 0)
            {
                sValue = sValue + S >= 255? S : 255 - sValue;
                sValue = 65025 / sValue - 255;
            }
            rgbM文章整理:學網 http://www.xue5.com (本站) [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30] [31] [32] [33] [34] [35] [36] [37] [38] [39] [40]

ax = rgbMax + (rgbMax - L) * sValue / 255;
            rgbMin = rgbMin + (rgbMin - L) * sValue / 255;
            rgbC = rgbC + (rgbC - L) * sValue / 255;
        }
    }
    if (bValue > 0)
    {
        rgbMax = rgbMax

e="COLOR: #000000">+ (255 - rgbMax) * bValue / 255;
        rgbMin = rgbMin + (255 - rgbMin) * bValue / 255;
        rgbC = rgbC + (255 - rgbC) * bValue / 255;
    }
    else if (bValue < 0)
    {
        rgbMax 

OR: #000000">= rgbMax + rgbMax * bValue / 255;
        rgbMin = rgbMin + rgbMin * bValue / 255;
        rgbC = rgbC + rgbC * bValue / 255;
    }
    CheckRGB(rgbMax);
    CheckRGB(rgbMin);
    CheckRGB(rgbC);
    if (bValue || S)
    {
        switch (H)
        {
          case 1: AssignRGB(R, G, B, rgbC, rgbMax,

 rgbMin);
          break;
          case 2: AssignRGB(R, G, B, rgbMin, rgbMax, rgbC);
          break;
          case 3: AssignRGB(R, G, B, rgbMin, rgbC, rgbMax);
          break;
          case 4: AssignRGB(R, G, B, rgbC, rgbMin, rgbMax);
          break;
          case 5: AssignRGB(R, G, B, rgbMax, rgbMin, rgbC);
          break;
          default:AssignRGB(R, G, B, rgbMax, rgbC, rgbMin);
        }
    }
//  

  else AssignRGB(R, G, B, rgbMax, rgbMin, rgbC);
}

void GdipHSBAdjustment(Bitmap *Bmp, int hValue, int sValue, int bValue)
{
    sValue = sValue * 255 / 100;
    bValue = bValue * 255 / 100;
    BitmapData data;
    Rect r(0, 0, Bmp->GetWidth(), Bmp->GetHeight());
    Bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite,

 PixelFormat24bppRGB, &data);
    try
    {
        int offset = data.Stride - data.Width * 3;
        unsigned char *p = (unsigned char*)data.Scan0;
        for (int y = 0; y < data.Height; y ++, p += offset)
            for (int x = 0; x 

OR: #000000">< data.Width; x ++, p += 3)
                SetRgbHsb(p[2], p[1], *p, hValue, sValue, bValue);
    }
    __finally
    {
        Bmp->UnlockBits(&data);
    }
}

#pragma package(smart_init)

// main.h

#ifndef mainH
#define mainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp

00000">>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include "RgbHsb.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TButton *Button1;
    TLabel *Label1;
    TLabel *Label2;
    TLabel *Label3;
    TTrackBar *HBar;
    TTrackBar *SBar;
    TTrackBar *BBar;
    TEdit 

an>*HEdit;
    TEdit *SEdit;
    TEdit *BEdit;
    TPaintBox *PaintBox1;
    void __fastcall PaintBox1Paint(TObject *Sender);
    void __fastcall HEditKeyPress(TObject *Sender, char &Key);
    void __fastcall HEditChange(TObject *Sender);
    void __fastcall HBarChange(TObject *Sender);
    void __fastcall SBarChange(TObject *Sender);
    void __fastcall BBarChange(TObject *Sender);
private:    // User declarations

0000ff">public:        // User declarations
    __fastcall TForm1(TComponent* Owner);
    __fastcall ~TForm1(void);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

// main.cpp


#include <vcl.h>
#pragma hdrstop

#include "main.h"
#include <stdlib.h>
//---------------------------------------------------------------

------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

ULONG gdiplusToken;
Bitmap *Bmp, *tmpBmp;
Gdiplus::Rect r;
bool lock;

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    Bmp = new Bitmap(WideString("100_0349.jpg"/*"d:\100_1.jpg"*/));
    r = Gdiplus::Rect(

0">0, 0, Bmp->GetWidth(), Bmp->GetHeight());
    tmpBmp = Bmp->Clone(r, PixelFormat24bppRGB);
    DoubleBuffered = true;
    lock = false;
}
__fastcall TForm1::~TForm1(void)
{
    delete tmpBmp;
    delete Bmp;
    GdiplusShutdown(gdiplusToken);
}
//---------------------------------------------------------------------------


void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
    Gdiplus::Graphics g(PaintBox1->Canvas->Handle);
    g.DrawImage(tmpBmp, r);
    g.TranslateTransform(0,r.Height);
    g.DrawImage(Bmp, r);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::HEditKeyPress(TObject *Sender, char &Key)
{
    if (Key >= 32 && (Key < 48 || Key > 57))
        Key = 0;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::HEditChange(TObject *Sender)
{
    lock 

style="COLOR: #000000">= true;
    if (((TEdit*)Sender)->Text.Length() == 0)
        ((TEdit*)Sender)->Text = "0";
    switch (((TEdit*)Sender)->Tag)
    {
        case 0: HBar->Position = HEdit->Text.ToInt();
        break;
        case 1: SBar->Position&nbs

p;= SEdit->Text.ToInt();
        break;
        case 2: BBar->Position = BEdit->Text.ToInt();
        break;
    }
    lock = false;
    delete tmpBmp;
    tmpBmp = Bmp->Clone(r, PixelFormat24bppRGB);
    if (HBar->Position || SBar->Position || BBar->Position)
        GdipHSBAdjustment(tmpBmp, HBar->Position, SBar->

style="COLOR: #000000">Position, BBar->Position);
    PaintBox1->Invalidate();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::HBarChange(TObject *Sender)
{
    if (!lock)
        HEdit->Text = HBar->Position;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::SBarChange(TObject *Sender)
{
    if (!lock)
        SEdit->Text = SBar->Position;
}


//---------------------------------------------------------------------------

void __fastcall TForm1::BBarChange(TObject *Sender)
{
    if (!lock)
        BEdit->Text = BBar->Position;
}

  由於本人文化水平太差,雖摸索出這些算法,但卻沒法把它進一步說透,只好說些“獨立”,“補丁”的字眼,讓各位見笑了。如有錯誤請指正,有建議也請來信:[email protected]

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved