Eğitim menüsüBu sayfadaki içindekiler tablosu

Mimarlık

Oz vatandaşları, hayırseverleri olan her şeye gücü yeten Büyücü'den oldukça memnundu. Onun bilgeliğini ve iyiliğini, gücünün kim, neden ve nerede olduğunu hiç sorgulamadan kabul ettiler. Oz vatandaşları gibi, ImageMagick'in perdenin arkasında neler olup bittiğini bilmeden resimlerinizi dönüştürmenize, düzenlemenize veya oluşturmanıza yardımcı olabileceğinden eminseniz, bu bölümü atlayabilirsiniz. Ancak, ImageMagick'in arkasındaki yazılım ve algoritmalar hakkında daha fazla bilgi edinmek istiyorsanız, okumaya devam edin. Bu tartışmadan tam olarak yararlanmak için, resim adlandırma konusunda rahat olmalı ve bilgisayar programlamaya aşina olmalısınız.

Mimari Genel Bakış

Bir resim genellikle piksellerden ve meta verilerden oluşan dikdörtgen bir bölgeden oluşur. Bir resmi verimli bir şekilde dönüştürmek, düzenlemek veya oluşturmak için, bölge içindeki (ve bazen bölge dışındaki) herhangi bir piksele kolayca erişmemiz gerekir. Ve bir resim dizisi durumunda, dizideki herhangi bir resmin herhangi bir bölgesindeki herhangi bir piksele erişmemiz gerekir. Ancak JPEG, TIFF, PNG, GIF vb. gibi yüzlerce resim formatı bulunması, piksellere istenildiği anda erişimi zorlaştırmaktadır. Bu formatlar içinde şunlarda farklılıklar buluyoruz:

  • renk alanı (örn. sRGB, doğrusal RGB, doğrusal GRİ, CMYK, YUV, Lab, vb.)
  • bit derinliği (örn. 1, 4, 8, 12, 16, vb.)
  • depolama biçimi (örn. imzasız, imzalı, kayan nokta, çift, vb.)
  • sıkıştırma (örn. sıkıştırılmamış, RLE, Zip, BZip, vb.)
  • yönlendirme (örn. yukarıdan aşağıya, sağdan sola, vb.),
  • düzen (örn. ham, işlem kodlarıyla serpiştirilmiş, vb.)

In ek olarak, bazı görüntü pikselleri zayıflama gerektirebilir, bazı formatlar birden fazla kareye izin verir ve bazı formatlar önce rasterleştirilmesi (vektörden piksele dönüştürülmesi) gereken vektör grafikleri içerir.

Bir görüntü işleme algoritmasının etkili bir şekilde uygulanması şunları elde etmemizi veya ayarlamamızı gerektirebilir:

  • bir seferde bir piksel (ör. 10,3 konumundaki piksel)
  • tek bir tarama çizgisi (ör. 4. satırdaki tüm pikseller)
  • aynı anda birkaç tarama çizgisi (ör. 4-7. satırlardaki pikseller)
  • tek bir sütun veya sütunlar piksel (ör. 11. sütundaki tüm pikseller)
  • görüntüden rastgele bir piksel bölgesi (ör. 10,7 ila 10,19 arasında tanımlanan pikseller)
  • rastgele sırada bir piksel (ör. 14,15 ve 640,480'deki piksel)
  • pikseller iki farklı görüntü (örn. görüntü 1'den 5,1'deki piksel ve görüntü 2'den 5,1'deki piksel)
  • görüntünün sınırları dışındaki pikseller (örn. -1,-3'teki piksel)
  • işaretsiz (65311) veya kayan nokta gösteriminde (örn. 0,17836) bir piksel bileşeni
  • negatif değerleri (örn. -0,0072973525628) ve kuantum derinliğini aşan değerleri (örn. 65931) içerebilen yüksek dinamik aralıklı bir piksel
  • yürütmenin farklı iş parçacıklarında aynı anda bir veya daha fazla piksel
  • CPU'lar, GPU'lar, ve diğer işlemciler
  • piksel kanalının kopyalanıp kopyalanmayacağını, güncellenip güncellenmeyeceğini veya karıştırılıp karıştırılmayacağını belirtmek için her kanalla ilişkilendirilen özellikler
  • hangi piksellerin güncellenmeye uygun olduğunu tanımlayan maskeler
  • kullanıcıya fayda sağlayan ancak ImageMagick görüntü işleme algoritmaları tarafından başka türlü dokunulmayan ekstra kanallar

Çeşitli görüntü formatları ve görüntü işleme gereksinimleri göz önüne alındığında, görüntü bölgesinin herhangi bir yerindeki (yani authentic pixels) ve bir dizideki herhangi bir görüntüden herhangi bir piksele talep üzerine uygun ardışık veya paralel erişim sağlamak için ImageMagick pixel cache'u uyguladık. Ek olarak, piksel önbelleği görüntü tarafından tanımlanan sınırların dışındaki piksellere erişime izin verir (yani virtual pixels).

Piksellere ek olarak, görüntülerde çok sayıda image properties and profiles bulunur. Özellikler, genişlik, yükseklik, derinlik ve renk alanı gibi iyi bilinen öznitelikleri içerir. Bir görüntü, görüntü yazarı, bir yorum, bir oluşturma tarihi ve diğerlerini içerebilen isteğe bağlı özelliklere sahip olabilir. Bazı görüntüler ayrıca renk yönetimi için profiller veya EXIF, IPTC, 8BIM veya XMP bilgi profilleri içerir. ImageMagick, görüntü özelliklerini veya profillerini almak, ayarlamak veya görüntülemek veya profilleri uygulamak için komut satırı seçenekleri ve programlama yöntemleri sağlar.

ImageMagick, yaklaşık yarım milyon satır C kodundan oluşur ve isteğe bağlı olarak bağımlı kitaplıklardaki (ör. JPEG, PNG, TIFF kitaplıkları) birkaç milyon satır koda bağlıdır. Bunu göz önünde bulundurarak, büyük bir mimari belge beklenebilir. Ancak, görüntü işlemenin büyük çoğunluğu yalnızca piksellere ve meta verilerine erişmektir ve basit, zarif ve etkili uygulamamız bunu ImageMagick geliştiricisi için kolaylaştırır. Piksel önbelleğinin uygulanmasını ve görüntü özelliklerini ve profillerini almayı ve ayarlamayı sonraki birkaç bölümde ele alacağız. Ardından, yürütmenin thread'ü içinde ImageMagick'i kullanmayı ele alacağız. Son bölümlerde, belirli bir görüntü biçimini okumak veya yazmak için image coders'ü ele alacağız ve ardından özel gereksinimlerinize göre piksellere erişmek veya pikselleri güncellemek için filter'i oluşturmaya ilişkin birkaç söz söyleyeceğiz.

Piksel Önbelleği

ImageMagick piksel önbelleği, 64 kanala kadar görüntü pikselleri için bir depodur. Kanallar, ImageMagick oluşturulurken belirtilen derinlikte bitişik olarak depolanır. Kanal derinlikleri, ImageMagick'in Q8 sürümü için piksel başına 8 bit bileşeni, Q16 sürümü için piksel başına 16 bit bileşeni ve Q32 sürümü için piksel başına 32 bit bileşenidir. Varsayılan olarak piksel bileşenleri 32 bit kayan bit high dynamic-range miktarlarıdır. Kanallar herhangi bir değeri tutabilir ancak genellikle kırmızı, yeşil, mavi ve alfa yoğunlukları veya camgöbeği, macenta, sarı, siyah ve alfa yoğunlukları içerir. Bir kanal, renk eşlemeli görüntüler için renk eşleme dizinlerini veya CMYK görüntüleri için siyah kanalı içerebilir. Piksel önbellek depolaması yığın belleği, disk destekli bellek eşlemeli veya diskte olabilir. Piksel önbelleği referans sayılır. Önbellek klonlandığında yalnızca önbellek özellikleri kopyalanır. Önbellek pikselleri daha sonra yalnızca piksellerden herhangi birini güncelleme niyetinizi bildirdiğinizde kopyalanır.

Piksel Önbelleğini Oluştur

Piksel önbelleği, oluşturulduğunda bir görüntüyle ilişkilendirilir ve pikselleri almaya veya koymaya çalıştığınızda başlatılır. Bir piksel önbelleğini bir görüntüyle ilişkilendirmek için üç yaygın yöntem şunlardır:

Arka plan rengine göre başlatılmış bir görüntü tuvali oluşturun:
image=AllocateImage(image_info);
if (SetImageExtent(image,640,480) == MagickFalse)
  { /* an exception was thrown */ }
(void) QueryMagickColor("red",&image->background_color,&image->exception);
SetImageBackgroundColor(image);
Diskteki bir JPEG görüntüsünden bir görüntü oluşturun:
(void) strcpy(image_info->filename,"image.jpg"):
image=ReadImage(image_info,exception);
if (image == (Image *) NULL)
  { /* an exception was thrown */ }
Bellek tabanlı bir görüntüden bir görüntü oluşturun:
image=BlobToImage(blob_info,blob,extent,exception);
if (image == (Image *) NULL)
  { /* an exception was thrown */ }

Piksel önbelleği tartışmamızda, noktalarımızı açıklamak için MagickCore API'yi kullanıyoruz, ancak ilkeler ImageMagick'e yönelik diğer program arayüzleri için de aynıdır.

Piksel önbelleği başlatıldığında, pikseller kaynaklandıkları bit derinliğinden piksel önbelleğinin gerektirdiği derinliğe ölçeklenir. Örneğin, ImageMagick'in Q8 sürümünü kullanıyorsanız 1 kanallı 1 bitlik tek renkli bir PBM görüntüsü 8 bitlik gri görüntüye ve Q16 sürümü için 16 bitlik RGBA'ya ölçeklenir. -version seçeneğiyle hangi sürüme sahip olduğunuzu belirleyebilirsiniz:

$ identify -versionVersion: ImageMagick 7.1.1-38 2024-05-05 Q16 https://imagemagick.org

Gördüğünüz gibi, piksel önbelleğinin rahatlığı bazen depolama (örneğin, 1 bitlik tek renkli bir görüntüyü 16 bit olarak depolamak israftır) ve hız (yani, tüm görüntüyü bellekte depolamak genellikle bir seferde bir piksel tarama çizgisine erişmekten daha yavaştır) açısından bir uzlaşma ile birlikte gelir. Çoğu durumda, piksel önbelleğinin avantajları genellikle dezavantajlarından daha ağır basar.

Piksel Önbelleğine Erişim

Piksel önbelleği bir görüntüyle ilişkilendirildikten sonra, genellikle pikselleri almak, güncellemek veya içine koymak istersiniz. Görüntü bölgesinin içindeki piksellere authentic pixels ve bölgenin dışındakilere virtual pixels adını veririz. Önbellekteki piksellere erişmek için şu yöntemleri kullanın:

Piksel önbelleğindeki pikselleri düzenlemek için tipik bir MagickCore kod parçacığı aşağıdadır. Örneğimizde, pikselleri giriş görüntüsünden çıkış görüntüsüne kopyalıyoruz ve yoğunluğu %10 azaltıyoruz:

const Quantum
  *p;

Quantum
  *q;

ssize_t
  x,
  y;

destination=CloneImage(source,source->columns,source->rows,MagickTrue,exception);
if (destination == (Image *) NULL)
  { /* an exception was thrown */ }
for (y=0; y < (ssize_t) source->rows; y++)
{
  p=GetVirtualPixels(source,0,y,source->columns,1,exception);
  q=GetAuthenticPixels(destination,0,y,destination->columns,1,exception);
  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)
    break;
  for (x=0; x < (ssize_t) source->columns; x++)
  {
    SetPixelRed(image,90*p->red/100,q);
    SetPixelGreen(image,90*p->green/100,q);
    SetPixelBlue(image,90*p->blue/100,q);
    SetPixelAlpha(image,90*p->opacity/100,q);
    p+=GetPixelChannels(source);
    q+=GetPixelChannels(destination);
  }
  if (SyncAuthenticPixels(destination,exception) == MagickFalse)
    break;
}
if (y < (ssize_t) source->rows)
  { /* an exception was thrown */ }

Kaynak görüntüyü klonlayarak hedef görüntüyü ilk oluşturduğumuzda, piksel önbelleği pikselleri kopyalanmaz. Bunlar yalnızca GetAuthenticPixels() veya QueueAuthenticPixels() çağırarak piksel önbelleğini değiştirme veya ayarlama niyetinizi bildirdiğinizde kopyalanır. Mevcut olanları güncellemek yerine yeni piksel değerleri ayarlamak istiyorsanız QueueAuthenticPixels() kullanın. Piksel değerlerini ayarlamak için GetAuthenticPixels() kullanabilirsiniz ancak bunun yerine QueueAuthenticPixels() kullanmak biraz daha verimlidir. Son olarak, güncellenen piksellerin piksel önbelleğine itildiğinden emin olmak için SyncAuthenticPixels() kullanın.

Her piksele,metacontent adı verilen keyfi içerikler atayabilirsiniz. Bu içeriğe erişmek için GetVirtualMetacontent() (içeriği okumak için) veya GetAuthenticMetacontent() (içeriği güncellemek için) kullanın. Örneğin, meta içeriği yazdırmak için şunu kullanın:

const void
  *metacontent;

for (y=0; y < (ssize_t) source->rows; y++)
{
  p=GetVirtualPixels(source,0,y,source->columns,1);
  if (p == (const Quantum *) NULL)
    break;
  metacontent=GetVirtualMetacontent(source);
  /* print meta content here */
}
if (y < (ssize_t) source->rows)
  /* an exception was thrown */

Piksel önbellek yöneticisi, görüntü piksellerine doğrudan veya dolaylı erişim verip vermeyeceğinize karar verir. Bazı durumlarda pikseller ara bir arabelleğe yerleştirilir ve bu nedenle, önbellekteki karşılık gelen piksellerin güncellenmesini garantilemek için bu arabelleğin piksel önbelleğine pushed gönderildiğinden emin olmak için SyncAuthenticPixels()'ı çağırmalısınız. Bu nedenle, bir seferde yalnızca bir tarama çizgisini veya birkaç piksel tarama çizgisini okumanızı veya güncellemenizi öneririz. Ancak, istediğiniz herhangi bir dikdörtgen piksel bölgesini alabilirsiniz. GetAuthenticPixels(), istediğiniz bölgenin görüntü alanının sınırları içinde olmasını gerektirir. 640 x 480 görüntü için, 479. satırda 640 piksellik bir tarama çizgisi alabilirsiniz ancak 480. satırda bir tarama çizgisi isterseniz, bir istisna döndürülür (satırlar 0'dan başlayarak numaralandırılır). GetVirtualPixels() bu kısıtlamaya sahip değildir. Örneğin,

p=GetVirtualPixels(source,-3,-3,source->columns+3,6,exception);

bazıları görüntü bölgesinin sınırları içinde olmasa bile, şikayet etmeden istediğiniz pikselleri verir.

Sanal Pikseller

İlgi duyulan bir pikselin etrafında bir piksel mahallesi gerektiren çok sayıda görüntü işleme algoritması vardır. Algoritma genellikle kenar pikselleri olarak bilinen görüntü sınırları etrafındaki piksellerin nasıl işleneceğiyle ilgili bir uyarı içerir. Sanal piksellerde, algoritmanız için hangi sanal piksel yönteminin en uygun olduğunu seçmek dışında özel kenar işleme konusunda endişelenmenize gerek yoktur.

Sanal piksellere erişim, MagickCore API'sinden SetImageVirtualPixelMethod() yöntemi veya komut satırından -virtual-pixel seçeneği tarafından kontrol edilir. Yöntemler şunları içerir:

arka plangörüntünün etrafındaki alan arka plan rengidir
siyahgörüntünün etrafındaki alan siyahtır
dama tahtası fayansıgörüntü ve arka plan rengiyle dönüşümlü kareler
titreşimrastgele olmayan 32x32 titrek desen
kenarkenar pikselini sonsuza doğru uzat (varsayılan)
grigörüntünün çevresindeki alan gridir
yatay-döşemegörüntüyü yatay olarak döşe, arka plan rengi yukarıda/aşağıda
yatay-döşeme-kenargörüntüyü yatay olarak döşe ve yan kenarı kopyala pikseller
aynagörüntüyü yansıt döşe
rastgelegörüntüden rastgele bir piksel seç
döşegörüntüyü döşe
saydamgörüntünün etrafındaki alan şeffaf siyahlık
dikey-döşegörüntüyü dikey olarak döşe, kenarlar arka plandır renk
dikey-fayans-kenarıGörüntüyü dikey olarak döşeyin ve yan kenar piksellerini çoğaltın
beyazGörüntüyü çevreleyen alan beyazdır

Önbellek Depolama ve Kaynak Gereksinimleri

ImageMagick piksel önbelleğinin bu basit ve zarif tasarımının depolama ve işleme hızı açısından bir maliyeti olduğunu unutmayın. Piksel önbellek depolama gereksinimleri, görüntünün alanı ve piksel bileşenlerinin bit derinliği ile ölçeklenir. Örneğin, 640 x 480 boyutunda bir görüntümüz varsa ve ImageMagick'in HDRI olmayan Q16 sürümünü kullanıyorsak, piksel önbelleği görüntü için width * height * bit-depth / 8 * channels bayt veya yaklaşık 2,3 mebibayt (yani 640 * 480 * 2 * 4) tüketir. Fena değil, ama ya görüntünüz 25000 x 25000 pikselse? Piksel önbelleği yaklaşık 4,7 gibibayt depolama alanı gerektirir. Of. ImageMagick, büyük görüntüleri bellek yerine diske önbelleğe alarak olası büyük depolama gereksinimlerini hesaba katar. Tipik olarak piksel önbelleği yığın belleği kullanarak bellekte saklanır. Yığın belleği tükenirse, diskte piksel önbelleğini oluşturur ve onu belleğe eşlemeye çalışırız. Bellek eşleme belleği tükenirse, basitçe standart disk G/Ç kullanırız. Disk depolama alanı bol ve ucuzdur, ancak aynı zamanda çok yavaştır; bellekteki piksellere erişmekten 1000 kat daha yavaştır. Disk tabanlı önbelleği bellek eşlemesi yaparsak, 5 kata kadar bazı hız iyileştirmeleri elde edebiliriz. Depolama ile ilgili bu kararlar, piksel önbellek yöneticisinin işletim sistemiyle müzakere etmesiyle automagically yapılır. Ancak, piksel önbellek yöneticisinin piksel önbelleğini nasıl tahsis ettiğini cache resource limits ile etkileyebilirsiniz. Sınırlar şunları içerir:

genişlikbir görüntünün maksimum genişliği. Bu sınırı aşarsanız bir istisna atılır ve işlem durdurulur.
yükseklikbir görüntünün maksimum yüksekliği. Bu sınırın aşılması durumunda bir istisna atılır ve işlem durdurulur.
alanpiksel önbellek belleğinde bulunabilen herhangi bir görüntünün bayt cinsinden maksimum alanı. Bu sınır aşılırsa, görüntü otomatik olarak diske önbelleğe alınır ve isteğe bağlı olarak belleğe eşlenir.
bellekyığından piksel önbelleği için ayrılacak bayt cinsinden maksimum bellek miktarı.
haritapiksel önbelleği için ayrılacak bayt cinsinden maksimum bellek miktarı.
diskpiksel önbelleği tarafından kullanılmasına izin verilen maksimum disk alanı miktarı (bayt cinsinden). Bu sınır aşılırsa, ölümcül bir istisna atılır ve tüm işlemler durur.
dosyalaraçık piksel önbelleği dosyalarının maksimum sayısı. Bu sınır aşıldığında, diske önbelleğe alınan sonraki pikseller kapatılır ve talep üzerine yeniden açılır. Bu davranış, piksel önbelleği açma/kapatma sistem çağrılarının sayısını azaltarak, hız cezası olmadan diskte aynı anda çok sayıda görüntüye erişilmesine izin verir.
threadparalel olarak çalışmasına izin verilen maksimum iş parçacığı sayısı. Sisteminiz bu değerden daha az sayıda iş parçacığı seçebilir. ImageMagick varsayılan olarak optimum sayıda iş parçacığı seçer, bu genellikle ana bilgisayarınızdaki çekirdek sayısıdır. Bu değeri 1 olarak ayarlayın ve tüm paralel bölgeler tek bir iş parçacığı tarafından yürütülür.
timeişlemin yürütülmesine izin verilen maksimum saniye sayısı. Bu sınırı aşarsanız bir istisna atılır ve işlem durur.

Dikkat edin, bu sınırlar ImageMagick piksel önbelleğiyle ilgilidir. ImageMagick içindeki belirli algoritmalar bu sınırlara uymaz ve harici temsilci kitaplıklarının hiçbiri (ör. JPEG, TIFF, vb.) uymaz.

Bu sınırların geçerli ayarını belirlemek için şu komutu kullanın:

-> identify -list resource
Resource limits:
  Width: 100MP
  Height: 100MP
  Area: 25.181GB
  Memory: 11.726GiB
  Map: 23.452GiB
  Disk: unlimited
  File: 768
  Thread: 12
  Throttle: 0
  Time: unlimited

Bu sınırları security policy (bkz. policy.xml), environment variable, -limit komut satırı seçeneği veya SetMagickResourceLimit() MagickCore API yöntemi olarak ayarlayabilirsiniz. Örneğin, ImageMagick'e yönelik çevrimiçi web arayüzümüz MagickStudio, hizmet reddi durumunu önlemeye yardımcı olmak için şu politika sınırlarını içerir:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policymap [
<!ELEMENT policymap (policy)*>
<!ATTLIST policymap xmlns CDATA #FIXED "">
<!ELEMENT policy EMPTY>
<!ATTLIST policy xmlns CDATA #FIXED "">
<!ATTLIST policy domain NMTOKEN #REQUIRED>
<!ATTLIST policy name NMTOKEN #IMPLIED>
<!ATTLIST policy pattern CDATA #IMPLIED>
<!ATTLIST policy rights NMTOKEN #IMPLIED>
<!ATTLIST policy stealth NMTOKEN #IMPLIED>
<!ATTLIST policy value CDATA #IMPLIED>
]>
<!--
  Creating a security policy that fits your specific local environment
  before making use of ImageMagick is highly advised. You can find guidance on
  setting up this policy at https://imagemagick.org/script/security-policy.php,
  and it's important to verify your policy using the validation tool located
  at https://imagemagick-secevaluator.doyensec.com/.


  Secure ImageMagick security policy:

  This stringent security policy prioritizes the implementation of
  rigorous controls and restricted resource utilization to establish a
  profoundly secure setting while employing ImageMagick. It deactivates
  conceivably hazardous functionalities, including specific coders like
  SVG or HTTP. The policy promotes the tailoring of security measures to
  harmonize with the requirements of the local environment and the guidelines
  of the organization. This protocol encompasses explicit particulars like
  limitations on memory consumption, sanctioned pathways for reading and
  writing, confines on image sequences, the utmost permissible duration of
  workflows, allocation of disk space intended for image data, and even an
  undisclosed passphrase for remote connections. By adopting this robust
  policy, entities can elevate their overall security stance and alleviate
  potential vulnerabilities.
-->
<policymap>
  <!-- Set maximum parallel threads. -->
  <policy domain="resource" name="thread" value="2"/>
  <!-- Set maximum time in seconds. When this limit is exceeded, an exception
       is thrown and processing stops. -->
  <policy domain="resource" name="time" value="120"/>
  <!-- Set maximum number of open pixel cache files. When this limit is
       exceeded, any subsequent pixels cached to disk are closed and reopened
       on demand. -->
  <policy domain="resource" name="file" value="768"/>
  <!-- Set maximum amount of memory in bytes to allocate for the pixel cache
       from the heap. When this limit is exceeded, the image pixels are cached
       to memory-mapped disk. -->
  <policy domain="resource" name="memory" value="256MiB"/>
  <!-- Set maximum amount of memory map in bytes to allocate for the pixel
       cache. When this limit is exceeded, the image pixels are cached to
       disk. -->
  <policy domain="resource" name="map" value="512MiB"/>
  <!-- Set the maximum width * height of an image that can reside in the pixel
       cache memory. Images that exceed the area limit are cached to disk. -->
  <policy domain="resource" name="area" value="16KP"/>
  <!-- Set maximum amount of disk space in bytes permitted for use by the pixel
       cache. When this limit is exceeded, the pixel cache is not be created
       and an exception is thrown. -->
  <policy domain="resource" name="disk" value="1GiB"/>
  <!-- Set the maximum length of an image sequence.  When this limit is
       exceeded, an exception is thrown. -->
  <policy domain="resource" name="list-length" value="32"/>
  <!-- Set the maximum width of an image.  When this limit is exceeded, an
       exception is thrown. -->
  <policy domain="resource" name="width" value="8KP"/>
  <!-- Set the maximum height of an image.  When this limit is exceeded, an
       exception is thrown. -->
  <policy domain="resource" name="height" value="8KP"/>
  <!-- Periodically yield the CPU for at least the time specified in
       milliseconds. -->
  <!--  -->
  <!-- Do not create temporary files in the default shared directories, instead
       specify a private area to store only ImageMagick temporary files. -->
  <!--  -->
  <!-- Force memory initialization by memory mapping select memory
       allocations. -->
  <policy domain="cache" name="memory-map" value="anonymous"/>
  <!-- Ensure all image data is fully flushed and synchronized to disk. -->
  <policy domain="cache" name="synchronize" value="true"/>
  <!-- Replace passphrase for secure distributed processing -->
  <!--  -->
  <!-- Do not permit any delegates to execute. -->
  <policy domain="delegate" rights="none" pattern="*"/>
  <!-- Do not permit any image filters to load. -->
  <policy domain="filter" rights="none" pattern="*"/>
  <!-- Don't read/write from/to stdin/stdout. -->
  <policy domain="path" rights="none" pattern="-"/>
  <!-- don't read sensitive paths. -->
  <policy domain="path" rights="none" pattern="/etc/*"/>
  <!-- Indirect reads are not permitted. -->
  <policy domain="path" rights="none" pattern="@*"/>
  <!-- These image types are security risks on read, but write is fine -->
  <policy domain="module" rights="write" pattern="{MSL,MVG,PS,SVG,URL,XPS}"/>
  <!-- This policy sets the number of times to replace content of certain
       memory buffers and temporary files before they are freed or deleted. -->
  <policy domain="system" name="shred" value="1"/>
  <!-- Enable the initialization of buffers with zeros, resulting in a minor
       performance penalty but with improved security. -->
  <policy domain="system" name="memory-map" value="anonymous"/>
  <!-- Set the maximum amount of memory in bytes that is permitted for
       allocation requests. -->
  <policy domain="system" name="max-memory-request" value="256MiB"/>
</policymap>

Birden fazla eşzamanlı oturumu işlediğimizden, hiçbir oturumun kullanılabilir belleğin tamamını tüketmesini istemiyoruz. Bu politika ile büyük resimler diske önbelleğe alınır. Resim çok büyükse ve piksel önbellek disk sınırını aşarsa program kapanır. Ayrıca, herhangi bir kaçak işleme görevini önlemek için bir zaman sınırı koyarız. Herhangi bir resmin genişliği veya yüksekliği 8192 pikseli aşarsa, bir istisna atılır ve işleme durur. ImageMagick 7.0.1-8 itibarıyla, herhangi bir temsilcinin veya tüm temsilcilerin kullanımını önleyebilirsiniz (deseni "*" olarak ayarlayın). Bu sürümden önce, temsilci kullanımını önlemek için "kodlayıcı" etki alanını kullanın (ör. domain="coder" rights="none" pattern="HTTPS"). Politika ayrıca dolaylı okumaları da engeller. Örneğin, bir dosyadan metin okumak istiyorsanız (ör. caption:@myCaption.txt), bu politikayı kaldırmanız gerekir.

Not, önbellek sınırları ImageMagick'in her çağrısı için geneldir, yani birkaç görüntü oluşturursanız, piksel önbellek depolama düzenini belirlemek için birleştirilmiş kaynak gereksinimleri sınırla karşılaştırılır.

Piksel önbelleğinin hangi tür ve ne kadar kaynak tükettiğini belirlemek için komut satırına -debug cache seçeneğini ekleyin:

$ magick -debug cache logo: -sharpen 3x2 null:
2016-12-17T13:33:42-05:00 0:00.000 0.000u 7.0.0 Cache magick: cache.c/DestroyPixelCache/1275/Cache
  destroy 
2016-12-17T13:33:42-05:00 0:00.000 0.000u 7.0.0 Cache magick: cache.c/OpenPixelCache/3834/Cache
  open LOGO[0] (Heap Memory, 640x480x4 4.688MiB)
2016-12-17T13:33:42-05:00 0:00.010 0.000u 7.0.0 Cache magick: cache.c/OpenPixelCache/3834/Cache
  open LOGO[0] (Heap Memory, 640x480x3 3.516MiB)
2016-12-17T13:33:42-05:00 0:00.010 0.000u 7.0.0 Cache magick: cache.c/ClonePixelCachePixels/1044/Cache
  Memory => Memory
2016-12-17T13:33:42-05:00 0:00.020 0.010u 7.0.0 Cache magick: cache.c/ClonePixelCachePixels/1044/Cache
  Memory => Memory
2016-12-17T13:33:42-05:00 0:00.020 0.010u 7.0.0 Cache magick: cache.c/OpenPixelCache/3834/Cache
  open LOGO[0] (Heap Memory, 640x480x3 3.516MiB)
2016-12-17T13:33:42-05:00 0:00.050 0.100u 7.0.0 Cache magick: cache.c/DestroyPixelCache/1275/Cache
  destroy LOGO[0]
2016-12-17T13:33:42-05:00 0:00.050 0.100u 7.0.0 Cache magick: cache.c/DestroyPixelCache/1275/Cache
  destroy LOGO[0]

Bu komut bellekteki bir piksel önbelleğini kullanır. Logo 4,688 MiB tüketti ve keskinleştirildikten sonra 3,516 MiB tüketti.

Dağıtılmış Piksel Önbelleği

Dağıtılmış piksel önbelleği, tek bir ana bilgisayarda bulunan geleneksel piksel önbelleğinin bir uzantısıdır. Dağıtılmış piksel önbelleği, çok büyük görüntüleri desteklemek için boyut ve işlem kapasitesi açısından büyüyebilmesi için birden fazla sunucuya yayılabilir. Bir veya daha fazla makinede piksel önbelleği sunucusunu başlatın. Bir görüntüyü okuduğunuzda veya üzerinde işlem yaptığınızda ve yerel piksel önbelleği kaynakları tükendiğinde, ImageMagick pikselleri depolamak veya almak için bu uzak piksel sunucularından bir veya daha fazlasıyla iletişim kurar. Dağıtılmış piksel önbelleği, pikselleri uzak sunucuya ve uzak sunucudan düzenlemek için ağ bant genişliğine güvenir. Bu nedenle, yerel depolama (örneğin bellek, disk vb.) kullanan bir piksel önbelleğinden önemli ölçüde daha yavaş olacaktır.

magick -distribute-cache 6668 &  // start on 192.168.100.50
magick -define registry:cache:hosts=192.168.100.50:6668 myimage.jpg -sharpen 5x2 mimage.png

Önbellek Görünümleri

MagickCore API'sinden GetVirtualPixels(), GetAuthenticPixels(), QueueAuthenticPixels() ve SyncAuthenticPixels(), aynı anda görüntü başına yalnızca bir piksel önbellek alanıyla ilgilenebilir. Aynı görüntüden aynı anda ilk ve son tarama çizgisine erişmek istediğinizi varsayalım? Çözüm cache view kullanmaktır. Önbellek görünümü, piksel önbelleğinde istediğiniz kadar alana aynı anda erişmenize izin verir. Önbellek görünümü methods, önceki yöntemlere benzerdir, ancak önce bir görünüm açmanız ve işiniz bittiğinde kapatmanız gerekir. İşte görüntünün ilk ve son piksel satırına aynı anda erişmemizi sağlayan MagickCore kodunun bir kesiti:

CacheView
  *first_row,
  *last_row;

first_row=AcquireVirtualCacheView(source,exception);
last_row=AcquireVirtualCacheView(source,exception);
for (y=0; y < (ssize_t) source->rows; y++)
{
  const Quantum
    *p,
    *q;

  p=GetCacheViewVirtualPixels(first_row,0,y,source->columns,1,exception);
  q=GetCacheViewVirtualPixels(last_row,0,source->rows-y-1,source->columns,1,exception);
  if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
    break;
  for (x=0; x < (ssize_t) source->columns; x++)
  {
    /* do something with p & q here */
  }
}
last_row=DestroyCacheView(last_row);
first_row=DestroyCacheView(first_row);
if (y < (ssize_t) source->rows)
  { /* an exception was thrown */ }

Magick Pixel Cache Format

Her görüntü biçiminin ImageMagick tarafından çözüldüğünü ve piksellerin piksel önbelleğine yerleştirildiğini hatırlayın. Bir görüntü yazarsanız, pikseller piksel önbelleğinden okunur ve yazdığınız biçime (ör. GIF, PNG, vb.) göre kodlanır. Magick Pixel Cache (MPC) biçimi, pikselleri bir görüntü biçimine ve biçiminden çözme ve kodlama yükünü ortadan kaldırmak için tasarlanmıştır. MPC iki dosya yazar. Uzantısı .mpc olan biri, görüntü veya görüntü dizisiyle ilişkili tüm özellikleri (ör. genişlik, yükseklik, renk alanı, vb.) korur ve uzantısı .cache olan ikincisi, yerel ham biçimdeki piksel önbelleğidir. Bir MPC görüntü dosyasını okurken, ImageMagick görüntü özelliklerini okur ve bellek, görüntü piksellerinin kodunu çözme ihtiyacını ortadan kaldırarak piksel önbelleğini diske eşler. Bunun karşılığı disk alanıdır. MPC genellikle diğer görüntü biçimlerinin çoğundan daha büyük bir dosya boyutuna sahiptir.

MPC görüntü dosyalarının en verimli kullanımı bir kez yazma, birçok kez okuma düzenidir. Örneğin, iş akışınız kaynak görüntüden rastgele piksel blokları çıkarmayı gerektirir. Kaynak görüntüyü her seferinde yeniden okumak ve muhtemelen sıkıştırmayı açmak yerine, MPC kullanırız ve görüntüyü doğrudan belleğe eşleriz.

Piksel Önbelleği Önerilen Uygulamalar

GetVirtualPixels(), GetAuthenticPixels(), QueueAuthenticPixels, GetCacheViewVirtualPixels(), GetCacheViewAuthenticPixels() ve QueueCacheViewAuthenticPixels() yöntemleriyle piksel önbelleğinden herhangi bir pikseli, herhangi bir piksel bloğunu, herhangi bir tarama çizgisini, birden fazla tarama çizgisini, herhangi bir satırı veya birden fazla satırı isteyebilseniz de, ImageMagick aynı anda birkaç piksel veya birkaç piksel satırı döndürmek üzere optimize edilmiştir. Tek bir tarama çizgisi veya aynı anda birkaç tarama çizgisi isterseniz ek optimizasyonlar da vardır. Bu yöntemler ayrıca piksel önbelleğine rastgele erişime izin verir, ancak ImageMagick sıralı erişim için optimize edilmiştir. Görüntünün son satırından ilkine doğru piksel tarama çizgilerine sırayla erişebilmenize rağmen, görüntünün ilk satırından son satırına doğru sıralı bir şekilde tarama çizgilerine erişirseniz performans artışı elde edebilirsiniz.

Pikselleri satır veya sütun sırasına göre alabilir, değiştirebilir veya ayarlayabilirsiniz. Ancak piksellere sütuna göre değil satıra göre erişmek daha verimlidir.

GetAuthenticPixels() veya GetCacheViewAuthenticPixels()'den döndürülen pikselleri güncellerseniz, değişikliklerinizin piksel önbelleğiyle senkronize olduğundan emin olmak için sırasıyla SyncAuthenticPixels() veya SyncCacheViewAuthenticPixels()'i çağırmayı unutmayın.

Başlangıç piksel değerini ayarlıyorsanız QueueAuthenticPixels() veya QueueCacheViewAuthenticPixels()'i kullanın. GetAuthenticPixels() veya GetCacheViewAuthenticPixels() yöntemi pikselleri önbellekten okur ve başlangıç piksel değerini ayarlıyorsanız bu okuma gereksizdir. Piksel değişikliklerini piksel önbelleğine göndermek için sırasıyla SyncAuthenticPixels() veya SyncCacheViewAuthenticPixels()'i çağırmayı unutmayın.

GetVirtualPixels(), GetAuthenticPixels(), QueueAuthenticPixels() ve SyncAuthenticPixels() önbellek görünümü muadillerinden biraz daha verimlidir. Ancak, görüntünün birden fazla bölgesine aynı anda erişmeniz gerekiyorsa veya görüntüye birden fazla thread of execution erişiyorsa önbellek görünümleri gerekir.

GetVirtualPixels() veya GetCacheViewVirtualPixels() ile görüntünün sınırları dışındaki pikselleri isteyebilirsiniz, ancak görüntü bölgesinin sınırları içindeki pikselleri istemek daha verimlidir.

Piksel önbelleğini uygun kaynak sınırlarını kullanarak diske zorlayabilmenize rağmen, disk erişimi bellek erişiminden 1000 kat daha yavaş olabilir. Piksel önbelleğine hızlı ve verimli erişim için piksel önbelleğini yığın belleğinde tutmaya çalışın.

ImageMagick'in ImageMagick Q16 sürümü, ölçekleme yapmadan 16 bit görüntüleri okumanıza ve yazmanıza izin verir ancak piksel önbelleği Q8 sürümünden iki kat daha fazla kaynak tüketir. Sisteminizde kısıtlı bellek veya disk kaynakları varsa, ImageMagick'in Q8 sürümünü düşünün. Ayrıca, Q8 sürümü genellikle Q16 sürümünden daha hızlı yürütülür.

Görüntü formatlarının ve algoritmalarının büyük çoğunluğu kendilerini 0'dan belirli bir maksimum değere kadar sabit bir piksel değeri aralığıyla sınırlar, örneğin ImageMagick'in Q16 sürümü 0'dan 65535'e kadar yoğunluklara izin verir. Ancak, yüksek dinamik aralıklı görüntüleme (HDRI), standart dijital görüntüleme tekniklerinden çok daha büyük bir pozlama dinamik aralığına (yani açık ve karanlık alanlar arasında büyük bir fark) izin verir. HDRI, en parlak doğrudan güneş ışığından en koyu en karanlık gölgelere kadar gerçek sahnelerde bulunan geniş yoğunluk seviyesi aralığını doğru bir şekilde temsil eder. Yüksek dinamik aralıklı görüntülerle başa çıkmak için ImageMagick derleme zamanında HDRI'ü etkinleştirin, ancak her piksel bileşeninin 32 bitlik kayan nokta değeri olduğunu unutmayın. Ek olarak, piksel değerleri varsayılan olarak sabitlenmez, bu nedenle bazı algoritmalar, HDRI olmayan sürüme göre bant dışı piksel değerleri nedeniyle beklenmeyen sonuçlar verebilir.

Büyük resimlerle uğraşıyorsanız, piksel önbelleğinin bol miktarda boş alana sahip bir disk alanına yazıldığından emin olun. Linux'ta bu genellikle /tmp ve Windows'ta c:/temp'dir. ImageMagick'e piksel önbelleğini alternatif bir konuma yazmasını ve şu seçeneklerle belleği korumasını söyleyebilirsiniz:

magick -limit memory 2GB -limit map 4GB -define registry:temporary-path=/data/tmp ...

policy.xml yapılandırma dosyasında ortamınız için genel kaynak sınırları belirleyin.

Aynı resmi birçok kez işlemeyi planlıyorsanız, MPC formatını göz önünde bulundurun. Bir MPC resminin okunması, resim piksellerinin kodunu çözme ihtiyacını ortadan kaldıran yerel piksel önbellek formatında olduğundan neredeyse sıfır ek yüke sahiptir. İşte bir örnek:

magick image.tif image.mpc
magick image.mpc -crop 100x100+0+0 +repage 1.png
magick image.mpc -crop 100x100+100+0 +repage 2.png
magick image.mpc -crop 100x100+200+0 +repage 3.png

MPC web siteleri için idealdir. Bir resmi okuma ve yazma ek yükünü azaltır. Bunu yalnızca online image studio'ümüzde kullanırız.

Görüntü Özellikleri ve Profilleri

Görüntüler, özellikler (ör. genişlik, yükseklik, açıklama vb.) ve profiller (ör. EXIF, IPTC, renk yönetimi) biçiminde meta verilerle ilişkilendirilir. ImageMagick, görüntü özelliklerini almak, ayarlamak veya güncellemek ve profilleri almak, ayarlamak, güncellemek veya uygulamak için kullanışlı yöntemler sağlar. Daha popüler görüntü özelliklerinin bazıları MagickCore API'sindeki Görüntü yapısıyla ilişkilendirilir. Örneğin:

(void) printf("image width: %lu, height: %lu\n",image->columns,image->rows);

Görüntü yorumu veya açıklaması gibi görüntü özelliklerinin büyük çoğunluğu için GetImageProperty() ve SetImageProperty() yöntemlerini kullanırız. Burada bir özelliği ayarlayıp hemen geri getiriyoruz:

const char
  *comment;

(void) SetImageProperty(image,"comment","This space for rent");
comment=GetImageProperty(image,"comment");
if (comment == (const char *) NULL)
  (void) printf("Image comment: %s\n",comment);

ImageMagick, GetImageArtifact() ve SetImageArtifact() yöntemleriyle eserleri destekler. Eserler, görüntü biçimlerine (ör. PNG) aktarılmayan gizli özelliklerdir.

Görüntü profilleri GetImageProfile(), SetImageProfile() ve ProfileImage() yöntemleriyle işlenir. Burada bir profil ayarlıyoruz ve hemen geri getiriyoruz:

StringInfo
  *profile;

profile=AcquireStringInfo(length);
SetStringInfoDatum(profile,my_exif_profile);
(void) SetImageProfile(image,"EXIF",profile);
DestroyStringInfo(profile);
profile=GetImageProfile(image,"EXIF");
if (profile != (StringInfo *) NULL)
  (void) PrintStringInfo(stdout,"EXIF",profile);

Çoklu Spektral Görüntüleme

ImageMagick, tüm kanalların orijinal görüntüyle aynı boyutlara ve piksel sayısına sahip olduğu multispectral imagery'i destekler. Ancak, tüm görüntü biçimleri çoklu spektral görüntüleri desteklemez. PSD, TIFF, MIFF, MPC ve FTXT, 31 banda kadar çoklu spektral görüntüler için tam desteğe sahiptir, bunların 21'i meta kanallardır. ImageMagick'i --enable-64bit-channel-masks yapılandırma betiği seçeneğiyle oluşturursanız, 52 meta kanala kadar 62 bantlı çoklu spektral görüntüleri işleyebilirsiniz.

Şu anda bir görüntü biçimi tarafından desteklenmeyen bir kullanım durumunuz varsa, bunu discussion forum'e gönderin. ImageMagick'in gelecekteki bir sürümünde kullanım durumunuzu desteklememiz için iyi bir şans var.

Akış Pikseller

ImageMagick, piksellerin bir görüntüden okunduğu veya görüntüye yazıldığı sırada akışını sağlar. Bunun piksel önbelleğine göre birkaç avantajı vardır. Piksel önbelleğinin tükettiği zaman ve kaynaklar bir görüntünün alanıyla ölçeklenirken, piksel akışı kaynakları bir görüntünün genişliğiyle ölçeklenir. Dezavantajı, piksellerin akış halindeyken tüketilmesi gerektiğidir, bu nedenle kalıcılık yoktur.

MagickCore programınızda pikselleri akış halindeyken tüketmek için uygun bir geri arama yöntemiyle ReadStream() veya WriteStream() kullanın. İşte ReadStream'i kullanmanın kısaltılmış bir örneği:

static size_t StreamPixels(const Image *image,const void *pixels,const size_t columns)
{
  register const Quantum
    *p;

  MyData
    *my_data;

  my_data=(MyData *) image->client_data;
  p=(Quantum *) pixels;
  if (p != (const Quantum *) NULL)
    {
      /* process pixels here */
    }
  return(columns);
}

...

/* invoke the pixel stream here */
image_info->client_data=(void *) MyData;
image=ReadStream(image_info,&StreamPixels,exception);

Ayrıca, görüntünün veya görüntünün bir bölümünün bir veya daha fazla piksel bileşenini seçtiğiniz depolama biçimine akış halinde aktarmak için hafif bir araç olan stream'ü de sağlıyoruz. Piksel bileşenlerini giriş görüntüsünden okundukça satır satır yazar, bu da büyük görüntülerle çalışırken veya ham piksel bileşenlerine ihtiyaç duyduğunuzda stream'i tercih edilir hale getirir. Görüntü biçimlerinin çoğu pikselleri (kırmızı, yeşil ve mavi) soldan sağa ve yukarıdan aşağıya doğru akıtır. Ancak birkaç biçim bu genel sıralamayı desteklemez (örneğin PSD biçimi).

Büyük Görüntü Desteği

ImageMagick, okuma, işleme ve yazma işlemlerini kapsayan megapikselden terapiksele kadar değişen görüntü boyutlarını işleme kapasitesine sahiptir. Teoride, görüntü boyutları 32 bitlik bir işletim sisteminde 31 milyon satır/sütun ve 64 bitlik bir işletim sisteminde tam 31 trilyona kadar uzayabilir. Ancak, gerçekte elde edilebilir boyutlar, ana bilgisayarınızda bulunan kaynaklara bağlı olarak önemli ölçüde daha azdır. Belirli görüntü biçimlerinin görüntü boyutuna sınırlamalar getirdiğinin farkında olmak önemlidir. Örneğin, Photoshop görüntüleri genişlik veya yükseklikte maksimum 300.000 pikselle sınırlıdır. Burada bir görüntüyü çeyrek milyon piksel kareye yeniden boyutlandırıyoruz:

magick logo: -resize 250000x250000 logo.miff

Büyük görüntüler için, bellek kaynakları muhtemelen tükenecektir ve ImageMagick bunun yerine diskte bir piksel önbelleği oluşturacaktır. Bol miktarda geçici disk alanınız olduğundan emin olun. Varsayılan geçici disk bölümünüz çok küçükse, ImageMagick'e bol miktarda boş alanı olan başka bir bölüm kullanmasını söyleyin. Örneğin:

magick -define registry:temporary-path=/data/tmp logo:  \      -resize 250000x250000 logo.miff

Büyük görüntülerin sisteminizdeki tüm belleği tüketmemesini sağlamak için, görüntü piksellerini kaynak sınırlamalarıyla bellek eşlemeli diske zorlayın:

magick -define registry:temporary-path=/data/tmp -limit memory 16mb \
  logo: -resize 250000x250000 logo.miff

Burada tüm görüntü piksellerini diske zorlarız:

magick -define registry:temporary-path=/data/tmp -limit area 0 \
  logo: -resize 250000x250000 logo.miff

Pikselleri diske önbelleğe almak, bellekten yaklaşık 1000 kat daha yavaştır. ImageMagick ile diskte büyük görüntüleri işlerken uzun çalışma süreleri bekleyin. İlerlemeyi şu komutla izleyebilirsiniz:

magick -monitor -limit memory 2GiB -limit map 4GiB -define registry:temporary-path=/data/tmp \
  logo: -resize 250000x250000 logo.miff

Gerçekten büyük görüntüler için veya ana bilgisayarınızda sınırlı kaynaklar varsa, bir veya daha fazla uzak ana bilgisayarda dağıtılmış piksel önbelleğini kullanabilirsiniz:

magick -distribute-cache 6668 &  // start on 192.168.100.50
magick -distribute-cache 6668 &  // start on 192.168.100.51
magick -limit memory 2mb -limit map 2mb -limit disk 2gb \
  -define registry:cache:hosts=192.168.100.50:6668,192.168.100.51:6668 \
  myhugeimage.jpg -sharpen 5x2 myhugeimage.png

Ağ gecikmesi nedeniyle, iş akışınızın işlenmesinde önemli bir yavaşlama bekleyin.

Yürütme İş Parçacıkları

ImageMagick'in dahili algoritmalarının çoğu, çok çekirdekli işlemci yongalarının sunduğu hız artışlarından yararlanmak için iş parçacıklıdır. Ancak, MagickCore'un GetVirtualPixels(), GetAuthenticPixels(), QueueAuthenticPixels() veya SyncAuthenticPixels() piksel önbellek yöntemleri hariç, yürütme iş parçacıklarınızda ImageMagick algoritmalarını kullanabilirsiniz. Bu yöntemler, OpenMP paralel bölümü hariç, yalnızca bir yürütme iş parçacığı için tasarlanmıştır. Birden fazla yürütme iş parçacığıyla piksel önbelleğine erişmek için bir önbellek görünümü kullanın. Bunu örneğin CompositeImage() yöntemi için yapıyoruz. Her yürütme iş parçacığında farklı bir hedef görüntü üzerinde tek bir kaynak görüntü birleştirmek istediğimizi varsayalım. GetVirtualPixels() kullanırsak, sonuçlar tahmin edilemez olur çünkü birden fazla iş parçacığı muhtemelen piksel önbelleğinin farklı alanlarını aynı anda ister. Bunun yerine, her bir yürütme iş parçacığı için benzersiz bir görünüm oluşturan ve programımızın kaç iş parçacığı çağrılırsa çağrılsın düzgün davranmasını sağlayan GetCacheViewVirtualPixels() kullanıyoruz. MagickWand API gibi diğer program arayüzleri tamamen iş parçacığı güvenlidir, bu nedenle yürütme iş parçacıkları için özel önlemler yoktur.

İşte OpenMP programlama paradigmasıyla yürütme iş parçacıklarından yararlanan bir MagickCore kod parçacığı:

CacheView
  *image_view;

MagickBooleanType
  status;

ssize_t
  y;

/*
  Acquire a cache view to enable parallelism.
*/
status=MagickTrue;
image_view=AcquireVirtualCacheView(image,exception);
#pragma omp parallel for schedule(static,4) shared(status)
for (y=0; y < (ssize_t) image->rows; y++)
{
  register Quantum
    *q;

  register ssize_t
    x;

  register void
    *metacontent;

  if (status == MagickFalse)
    continue;
  /*
    Get a row of pixels.
  */
  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
  if (q == (Quantum *) NULL)
    {
      status=MagickFalse;
      continue;
    }
  metacontent=GetCacheViewAuthenticMetacontent(image_view);
  for (x=0; x < (ssize_t) image->columns; x++)
  {
    /*
      Set the pixel color.
    */
    SetPixelRed(image,...,q);
    SetPixelGreen(image,...,q);
    SetPixelBlue(image,...,q);
    SetPixelAlpha(image,...,q);
    if (metacontent != NULL)
      metacontent[indexes+x]=...;
    q+=GetPixelChannels(image);
  }
  /*
    Sync the updated pixels to the pixel cache.
  */
  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
    status=MagickFalse;
}
/*
  Destroy the cache view.
*/
image_view=DestroyCacheView(image_view);
if (status == MagickFalse)
  perror("something went wrong");

Bu kod parçacığı sıkıştırılmamış bir Windows bit eşlemini bir Magick++ görüntüsüne dönüştürür:

#include "Magick++.h"
#include <assert.h>
#include "omp.h"

void ConvertBMPToImage(const BITMAPINFOHEADER *bmp_info,
  const unsigned char *restrict pixels,Magick::Image *image)
{
  /*
    Prepare the image so that we can modify the pixels directly.
  */
  assert(bmp_info->biCompression == BI_RGB);
  assert(bmp_info->biWidth == image->columns());
  assert(abs(bmp_info->biHeight) == image->rows());
  image->modifyImage();
  if (bmp_info->biBitCount == 24)
    image->type(MagickCore::TrueColorType);
  else
    image->type(MagickCore::TrueColorMatteType);
  register unsigned int bytes_per_row=bmp_info->biWidth*bmp_info->biBitCount/8;
  if (bytes_per_row % 4 != 0) {
    bytes_per_row=bytes_per_row+(4-bytes_per_row % 4);  // divisible by 4.
  }
  /*
    Copy all pixel data, row by row.
  */
  #pragma omp parallel for
  for (int y=0; y < int(image->rows()); y++)
  {
    int
      row;

    register const unsigned char
      *restrict p;

    register MagickCore::Quantum
      *restrict q;

    row=(bmp_info->biHeight > 0) ? (image->rows()-y-1) : y;
    p=pixels+row*bytes_per_row;
    q=image->setPixels(0,y,image->columns(),1);
    for (int x=0; x < int(image->columns()); x++)
    {
      SetPixelBlue(image,p[0],q);
      SetPixelGreen(image,p[1],q);
      SetPixelRed(image,p[2],q);
      if (bmp_info->biBitCount == 32) {
        SetPixelAlpha(image,p[3],q);
      }
      q+=GetPixelChannels(image);
      p+=bmp_info->biBitCount/8;
    }
    image->syncPixels();  // sync pixels to pixel cache.
  }
  return;
}

OpenMP etkin uygulamanızdan ImageMagick API'sini çağırırsanız ve sonraki paralel bölgelerdeki kullanılabilir iş parçacığı sayısını dinamik olarak artırmayı düşünüyorsanız, API'yi çağırdığınızda before artırma işlemini gerçekleştirdiğinizden emin olun, aksi takdirde ImageMagick hata verebilir.

MagickWand, değnek görünümlerini destekler. Bir görünüm, görüntünün tamamı veya bir kısmı üzerinde paralel olarak yineleme yapar ve her piksel satırı için sağladığınız bir geri arama yöntemini çağırır. Bu, paralel programlama etkinliğinizin çoğunu yalnızca o modülle sınırlar. MagickCore'da benzer yöntemler vardır. Bir örnek için, hem MagickWand hem de MagickCore'de uygulanan aynı sigmoidal kontrast algoritmasına bakın.

Çoğu durumda, varsayılan iş parçacığı sayısı, optimum performans için sisteminizdeki işlemci çekirdeği sayısına ayarlanır. Ancak, sisteminiz hiper iş parçacıklıysa veya sanal bir ana bilgisayarda çalışıyorsanız ve işlemcilerin yalnızca bir alt kümesi sunucu örneğiniz için kullanılabilirse, iş parçacığını policy veya MAGICK_THREAD_LIMIT ortam değişkenini ayarlayarak performansta bir artış elde edebilirsiniz. Örneğin, sanal ana bilgisayarınızda 8 işlemci var ancak sunucu örneğinize yalnızca 2 tanesi atanmış. Varsayılan 8 iş parçacığı sayısı ciddi performans sorunlarına neden olabilir. Bir çözüm, iş parçacığı sayısını policy.xml yapılandırma dosyanızdaki kullanılabilir işlemcilerle sınırlamaktır:

<policy domain="resource" name="thread" value="2"/>

Ya da 12 çekirdekli hiper iş parçacıklı bilgisayarınızın varsayılan olarak 24 iş parçacığına sahip olduğunu varsayalım. MAGICK_THREAD_LIMIT ortam değişkenini ayarlayın ve muhtemelen gelişmiş performans elde edeceksiniz:

export MAGICK_THREAD_LIMIT=12

OpenMP komitesi, OpenMP'yi Posix iş parçacıkları gibi diğer iş parçacığı modelleriyle karıştırmanın davranışını tanımlamamıştır. Ancak, Linux'un modern sürümlerini kullanarak, OpenMP ve Posix iş parçacıkları şikayetsiz bir şekilde birlikte çalışıyor gibi görünmektedir. Mac OS X veya daha eski bir Linux sürümünden ImageMagick uygulama programlama arayüzlerinden birini (ör. MagickCore, MagickWand, Magick++, vb.) çağıran bir program modülünden Posix iş parçacıklarını kullanmak istiyorsanız, ImageMagick içinde OpenMP desteğini devre dışı bırakmanız gerekebilir. --disable-openmp seçeneğini yapılandırma betiği komut satırına ekleyin ve ImageMagick'i yeniden oluşturun ve yeniden yükleyin.

tcmalloc bellek ayırma kitaplığıyla kilit çekişmesini azaltarak performansı daha da artırabilirsiniz. Etkinleştirmek için, ImageMagick'i oluştururken configure komut satırına --with-tcmalloc ekleyin.

Threading Performansı

Paralel bir ortamda davranışı tahmin etmek zor olabilir. Performans, derleyici, OpenMP kitaplığının sürümü, işlemci türü, çekirdek sayısı, bellek miktarı, hyperthreading'in etkin olup olmadığı, ImageMagick ile aynı anda yürütülen uygulama karışımı veya kullandığınız belirli görüntü işleme algoritması gibi bir dizi faktöre bağlı olabilir. İş parçacığı sayısı açısından en iyi performanstan emin olmanın tek yolu kıyaslama yapmaktır. ImageMagick, bir komutu kıyaslarken aşamalı iş parçacığını içerir ve bir veya daha fazla iş parçacığı için geçen süreyi ve verimliliği döndürür. Bu, ortamınızda kaç iş parçacığının en verimli olduğunu belirlemenize yardımcı olabilir. Bu kıyaslama için bir modelin 1920x1080 görüntüsünü 1 ila 12 iş parçacığıyla 10 kez keskinleştiriyoruz:

$ magick -bench 10 model.png -sharpen 5x2 null:
Performance[1]: 10i 1.135ips 1.000e 8.760u 0:08.810
Performance[2]: 10i 2.020ips 0.640e 9.190u 0:04.950
Performance[3]: 10i 2.786ips 0.710e 9.400u 0:03.590
Performance[4]: 10i 3.378ips 0.749e 9.580u 0:02.960
Performance[5]: 10i 4.032ips 0.780e 9.580u 0:02.480
Performance[6]: 10i 4.566ips 0.801e 9.640u 0:02.190
Performance[7]: 10i 3.788ips 0.769e 10.980u 0:02.640
Performance[8]: 10i 4.115ips 0.784e 12.030u 0:02.430
Performance[9]: 10i 4.484ips 0.798e 12.860u 0:02.230
Performance[10]: 10i 4.274ips 0.790e 14.830u 0:02.340
Performance[11]: 10i 4.348ips 0.793e 16.500u 0:02.300
Performance[12]: 10i 4.525ips 0.799e 18.320u 0:02.210

Bu örnek için en iyi nokta 6 iş parçacığıdır. 6 fiziksel çekirdek olduğu için bu mantıklıdır. Diğer 6'sı hiper iş parçacıklarıdır. Keskinleştirmenin hiper iş parçacığından faydalanmadığı anlaşılıyor.

Bazı durumlarda, iş parçacığı sayısını 1 olarak ayarlamak veya OpenMP'yi MAGICK_THREAD_LIMIT ortam değişkeni, -limit komut satırı seçeneği veya policy.xml yapılandırma dosyasıyla tamamen devre dışı bırakmak en iyi çözüm olabilir.

Heterojen Dağıtılmış İşleme

ImageMagick, OpenCL çerçevesiyle heterojen dağıtılmış işleme desteği içerir. ImageMagick içindeki OpenCL çekirdekleri, görüntü işleme algoritmalarının CPU'lar, GPU'lar ve diğer işlemcilerden oluşan heterojen platformlarda yürütülmesine izin verir. Platformunuza bağlı olarak, hızlanmalar geleneksel tek CPU'dan bir mertebe daha hızlı olabilir.

Öncelikle ImageMagick sürümünüzün OpenCL özelliğini desteklediğini doğrulayın:

magick identify -version
Features: DPC Cipher Modules OpenCL OpenMP(4.5)

Eğer öyleyse, görüntü evrişimi için önemli bir hız artışı elde etmek için şu komutu çalıştırın:

magick image.png -convolve '-1, -1, -1, -1, 9, -1, -1, -1, -1' convolve.png

Bir hızlandırıcı mevcut değilse veya hızlandırıcı yanıt vermezse, ImageMagick hızlandırılmamış evrişim algoritmasına geri döner.

İşte bir görüntüyü evriştiren bir OpenCL çekirdeği örneği:

static inline long ClampToCanvas(const long offset,const ulong range)
{
  if (offset < 0L)
    return(0L);
  if (offset >= range)
    return((long) (range-1L));
  return(offset);
}

static inline CLQuantum ClampToQuantum(const float value)
{
  if (value < 0.0)
    return((CLQuantum) 0);
  if (value >= (float) QuantumRange)
    return((CLQuantum) QuantumRange);
  return((CLQuantum) (value+0.5));
}

__kernel void Convolve(const __global CLPixelType *source,__constant float *filter,
  const ulong width,const ulong height,__global CLPixelType *destination)
{
  const ulong columns = get_global_size(0);
  const ulong rows = get_global_size(1);

  const long x = get_global_id(0);
  const long y = get_global_id(1);

  const float scale = (1.0/QuantumRange);
  const long mid_width = (width-1)/2;
  const long mid_height = (height-1)/2;
  float4 sum = { 0.0, 0.0, 0.0, 0.0 };
  float gamma = 0.0;
  register ulong i = 0;

  for (long v=(-mid_height); v <= mid_height; v++)
  {
    for (long u=(-mid_width); u <= mid_width; u++)
    {
      register const ulong index=ClampToCanvas(y+v,rows)*columns+ClampToCanvas(x+u,
        columns);
      const float alpha=scale*(QuantumRange-source[index].w);
      sum.x+=alpha*filter[i]*source[index].x;
      sum.y+=alpha*filter[i]*source[index].y;
      sum.z+=alpha*filter[i]*source[index].z;
      sum.w+=filter[i]*source[index].w;
      gamma+=alpha*filter[i];
      i++;
    }
  }

  gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
  const ulong index=y*columns+x;
  destination[index].x=ClampToQuantum(gamma*sum.x);
  destination[index].y=ClampToQuantum(gamma*sum.y);
  destination[index].z=ClampToQuantum(gamma*sum.z);
  destination[index].w=ClampToQuantum(sum.w);
};

OpenCL çekirdeğiyle görüntü evrişiminin tam bir uygulaması için MagickCore/accelerate.c'e bakın.

Windows'ta TDR (GPU'ların Zaman Aşımı Algılaması ve Kurtarılması) ile ilgili bir sorun yaşayabileceğinizi unutmayın. Amacı, yürütme süresi eşiğini kullanarak GPU'yu askıya alan kaçak görevleri algılamaktır. ImageMagick'te OpenCL filtrelerini çalıştıran bazı eski düşük uçlu GPU'lar için, daha uzun yürütme süreleri TDR mekanizmasını tetikleyebilir ve GPU görüntü filtresini önceden engelleyebilir. Bu olduğunda, ImageMagick otomatik olarak CPU kod yoluna geri döner ve beklenen sonuçları döndürür. Önceden engellemeyi önlemek için TdrDelay kayıt defteri anahtarını artırın.

Özel Görüntü Kodlayıcıları

Bir görüntü kodlayıcısı (yani kodlayıcı / kod çözücü), bir görüntü biçimini (ör. PNG, GIF, JPEG, vb.) kaydetmekten, isteğe bağlı olarak sınıflandırmaktan, isteğe bağlı olarak okumaktan, isteğe bağlı olarak yazmaktan ve kaydını silmekten sorumludur. Bir görüntü kodlayıcısını kaydetmek, ImageMagick'e belirli bir biçimin okunabilir veya yazılabilir olduğunu bildirir. Kayıttan çıkarma, ImageMagick'e formatın artık mevcut olmadığını söyler. Sınıflandırma yöntemi, bir görüntünün ilk birkaç baytına bakar ve görüntünün beklenen formatta olup olmadığını belirler. Okuyucu, görüntü boyutunu, renk alanını ve diğer özellikleri ayarlar ve piksel önbelleğini piksellerle yükler. Okuyucu, tek bir görüntü veya bir görüntü dizisi (format dosya başına birden fazla görüntüyü destekliyorsa) veya bir hata oluşursa bir istisna ve boş bir görüntü döndürür. Yazıcı bunun tersini yapar. Görüntü özelliklerini alır, piksel önbelleğini boşaltır ve bunları görüntü formatının gerektirdiği şekilde yazar.

İşte bir örnek custom coder listesi. Görüntüleri, basitçe bir kimlik, ardından görüntü genişliği ve yüksekliği ve ardından RGB piksel değerleri olan MGK görüntü formatında okur ve yazar.

#include <MagickCore/studio.h>
#include <MagickCore/blob.h>
#include <MagickCore/cache.h>
#include <MagickCore/colorspace.h>
#include <MagickCore/exception.h>
#include <MagickCore/image.h>
#include <MagickCore/list.h>
#include <MagickCore/magick.h>
#include <MagickCore/memory_.h>
#include <MagickCore/monitor.h>
#include <MagickCore/pixel-accessor.h>
#include <MagickCore/string_.h>
#include <MagickCore/module.h>
#include "filter/blob-private.h"
#include "filter/exception-private.h"
#include "filter/image-private.h"
#include "filter/monitor-private.h"
#include "filter/quantum-private.h"

/*
  Forward declarations.
*/
static MagickBooleanType
  WriteMGKImage(const ImageInfo *,Image *,ExceptionInfo *);

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   I s M G K                                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  IsMGK() returns MagickTrue if the image format type, identified by the
%  magick string, is MGK.
%
%  The format of the IsMGK method is:
%
%      MagickBooleanType IsMGK(const unsigned char *magick,const size_t length)
%
%  A description of each parameter follows:
%
%    o magick: This string is generally the first few bytes of an image file
%      or blob.
%
%    o length: Specifies the length of the magick string.
%
*/
static MagickBooleanType IsMGK(const unsigned char *magick,const size_t length)
{
  if (length < 7)
    return(MagickFalse);
  if (LocaleNCompare((char *) magick,"id=mgk",7) == 0)
    return(MagickTrue);
  return(MagickFalse);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d M G K I m a g e                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  ReadMGKImage() reads a MGK image file and returns it.  It allocates the
%  memory necessary for the new Image structure and returns a pointer to the
%  new image.
%
%  The format of the ReadMGKImage method is:
%
%      Image *ReadMGKImage(const ImageInfo *image_info,
%        ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o image_info: the image info.
%
%    o exception: return any errors or warnings in this structure.
%
*/
static Image *ReadMGKImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
  char
    buffer[MaxTextExtent];

  Image
    *image;

  long
    y;

  MagickBooleanType
    status;

  register long
    x;

  register Quantum
    *q;

  register unsigned char
    *p;

  ssize_t
    count;

  unsigned char
    *pixels;

  unsigned long
    columns,
    rows;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Read MGK image.
  */
  (void) ReadBlobString(image,buffer);  /* read magic number */
  if (IsMGK(buffer,7) == MagickFalse)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  (void) ReadBlobString(image,buffer);
  count=(ssize_t) sscanf(buffer,"%lu %lu\n",&columns,&rows);
  if (count <= 0)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  do
  {
    /*
      Initialize image structure.
    */
    image->columns=columns;
    image->rows=rows;
    image->depth=8;
    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    /*
      Convert MGK raster image to pixel packets.
    */
    if (SetImageExtent(image,image->columns,image->rows,exception) == MagickFalse)
      return(DestroyImageList(image));
    pixels=(unsigned char *) AcquireQuantumMemory((size_t) image->columns,
      3UL*sizeof(*pixels));
    if (pixels == (unsigned char *) NULL)
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    for (y=0; y < (long) image->rows; y++)
    {
      count=(ssize_t) ReadBlob(image,(size_t) (3*image->columns),pixels);
      if (count != (ssize_t) (3*image->columns))
        ThrowReaderException(CorruptImageError,"UnableToReadImageData");
      p=pixels;
      q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
      if (q == (Quantum *) NULL)
        break;
      for (x=0; x < (long) image->columns; x++)
      {
        SetPixelRed(image,ScaleCharToQuantum(*p++),q);
        SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
        SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
        q+=GetPixelChannels(image);
      }
      if (SyncAuthenticPixels(image,exception) == MagickFalse)
        break;
      if (image->previous == (Image *) NULL)
        if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
            (QuantumTick(y,image->rows) != MagickFalse))
          {
            status=image->progress_monitor(LoadImageTag,y,image->rows,
              image->client_data);
            if (status == MagickFalse)
              break;
          }
    }
    pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    if (EOFBlob(image) != MagickFalse)
      {
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
        break;
      }
    /*
      Proceed to next image.
    */
    if (image_info->number_scenes != 0)
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    *buffer='\0';
    (void) ReadBlobString(image,buffer);
    count=(ssize_t) sscanf(buffer,"%lu %lu\n",&columns,&rows);
    if (count > 0)
      {
        /*
          Allocate next image structure.
        */
        AcquireNextImage(image_info,image,exception);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            image=DestroyImageList(image);
            return((Image *) NULL);
          }
        image=SyncNextImageInList(image);
        if (image->progress_monitor != (MagickProgressMonitor) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,TellBlob(image),
              GetBlobSize(image));
            if (status == MagickFalse)
              break;
          }
      }
  } while (count > 0);
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e g i s t e r M G K I m a g e                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  RegisterMGKImage() adds attributes for the MGK image format to
%  the list of supported formats.  The attributes include the image format
%  tag, a method to read and/or write the format, whether the format
%  supports the saving of more than one frame to the same file or blob,
%  whether the format supports native in-memory I/O, and a brief
%  description of the format.
%
%  The format of the RegisterMGKImage method is:
%
%      unsigned long RegisterMGKImage(void)
%
*/
ModuleExport unsigned long RegisterMGKImage(void)
{
  MagickInfo
    *entry;

  entry=AcquireMagickInfo("MGK","MGK","MGK image");
  entry->decoder=(DecodeImageHandler *) ReadMGKImage;
  entry->encoder=(EncodeImageHandler *) WriteMGKImage;
  entry->magick=(IsImageFormatHandler *) IsMGK;
  (void) RegisterMagickInfo(entry);
  return(MagickImageCoderSignature);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   U n r e g i s t e r M G K I m a g e                                       %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  UnregisterMGKImage() removes format registrations made by the
%  MGK module from the list of supported formats.
%
%  The format of the UnregisterMGKImage method is:
%
%      UnregisterMGKImage(void)
%
*/
ModuleExport void UnregisterMGKImage(void)
{
  (void) UnregisterMagickInfo("MGK");
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   W r i t e M G K I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  WriteMGKImage() writes an image to a file in red, green, and blue MGK
%  rasterfile format.
%
%  The format of the WriteMGKImage method is:
%
%      MagickBooleanType WriteMGKImage(const ImageInfo *image_info,
%        Image *image)
%
%  A description of each parameter follows.
%
%    o image_info: the image info.
%
%    o image:  The image.
%
%    o exception:  return any errors or warnings in this structure.
%
*/
static MagickBooleanType WriteMGKImage(const ImageInfo *image_info,Image *image,
  ExceptionInfo *exception)
{
  char
    buffer[MaxTextExtent];

  long
    y;

  MagickBooleanType
    status;

  MagickOffsetType
    scene;

  register const Quantum
    *p;

  register long
    x;

  register unsigned char
    *q;

  unsigned char
    *pixels;

  /*
    Open output image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
  if (status == MagickFalse)
    return(status);
  scene=0;
  do
  {
    /*
      Allocate memory for pixels.
    */
    if (image->colorspace != RGBColorspace)
      (void) SetImageColorspace(image,RGBColorspace,exception);
    pixels=(unsigned char *) AcquireQuantumMemory((size_t) image->columns,
      3UL*sizeof(*pixels));
    if (pixels == (unsigned char *) NULL)
      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    /*
      Initialize raster file header.
    */
    (void) WriteBlobString(image,"id=mgk\n");
    (void) FormatLocaleString(buffer,MaxTextExtent,"%lu %lu\n",image->columns,
       image->rows);
    (void) WriteBlobString(image,buffer);
    for (y=0; y < (long) image->rows; y++)
    {
      p=GetVirtualPixels(image,0,y,image->columns,1,exception);
      if (p == (const Quantum *) NULL)
        break;
      q=pixels;
      for (x=0; x < (long) image->columns; x++)
      {
        *q++=ScaleQuantumToChar(GetPixelRed(image,p));
        *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
        *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
        p+=GetPixelChannels(image);
      }
      (void) WriteBlob(image,(size_t) (q-pixels),pixels);
      if (image->previous == (Image *) NULL)
        if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
            (QuantumTick(y,image->rows) != MagickFalse))
          {
            status=image->progress_monitor(SaveImageTag,y,image->rows,
              image->client_data);
            if (status == MagickFalse)
              break;
          }
    }
    pixels=(unsigned char *) RelinquishMagickMemory(pixels);
    if (GetNextImageInList(image) == (Image *) NULL)
      break;
    image=SyncNextImageInList(image);
    status=SetImageProgress(image,SaveImagesTag,scene,
      GetImageListLength(image));
    if (status == MagickFalse)
      break;
    scene++;
  } while (image_info->adjoin != MagickFalse);
  (void) CloseBlob(image);
  return(MagickTrue);
}

Özel kodlayıcıyı komut satırından çağırmak için şu komutları kullanın:

magick logo: logo.mgk
display logo.mgk

Kendi özel kodlayıcınızı yazmaya başlamanıza yardımcı olmak için Magick Coder Kit'ü sağlıyoruz.

Derlemeden önce, ImageMagick varsayılan sistem yolunuzda değilse PKG_CONFIG_PATH ortam değişkenini ayarlayın:

export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 

Özel Görüntü Filtreleri

ImageMagick, kendi özel görüntü işleme algoritmalarınızı eklemek için kullanışlı bir mekanizma sağlar. Bunlara görüntü filtreleri diyoruz ve komut satırından -process seçeneğiyle veya MagickCore API yöntemi ExecuteModuleProcess()'dan çağrılıyorlar.

İşte bir örnek custom image filter'nin listesi. Piksel parlaklığı ve doygunluk ortalaması ve standart sapma gibi birkaç istatistiği hesaplar.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <math.h>
#include "MagickCore/studio.h"
#include "MagickCore/MagickCore.h"

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   a n a l y z e I m a g e                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  analyzeImage() computes the brightness and saturation mean,  standard
%  deviation, kurtosis and skewness and stores these values as attributes 
%  of the image.
%
%  The format of the analyzeImage method is:
%
%      size_t analyzeImage(Image *images,const int argc,char **argv,
%        ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o image: the address of a structure of type Image.
%
%    o argc: Specifies a pointer to an integer describing the number of
%      elements in the argument vector.
%
%    o argv: Specifies a pointer to a text array containing the command line
%      arguments.
%
%    o exception: return any errors or warnings in this structure.
%
*/

typedef struct _StatisticsInfo
{
  double
    area,
    brightness,
    mean,
    standard_deviation,
    sum[5],
    kurtosis,
    skewness;
} StatisticsInfo;

static inline int GetMagickNumberThreads(const Image *source,
  const Image *destination,const size_t chunk,int multithreaded)
{
#define MagickMax(x,y)  (((x) > (y)) ? (x) : (y))
#define MagickMin(x,y)  (((x) < (y)) ? (x) : (y))

  /*
    Number of threads bounded by the amount of work and any thread resource
    limit.  The limit is 2 if the pixel cache type is not memory or
    memory-mapped.
  */
  if (multithreaded == 0)
    return(1);
  if (((GetImagePixelCacheType(source) != MemoryCache) &&
       (GetImagePixelCacheType(source) != MapCache)) ||
      ((GetImagePixelCacheType(destination) != MemoryCache) &&
       (GetImagePixelCacheType(destination) != MapCache)))
    return(MagickMax(MagickMin(GetMagickResourceLimit(ThreadResource),2),1));
  return(MagickMax(MagickMin((ssize_t) GetMagickResourceLimit(ThreadResource),
    (ssize_t) (chunk)/64),1));
}

ModuleExport size_t analyzeImage(Image **images,const int argc,
  const char **argv,ExceptionInfo *exception)
{
#define AnalyzeImageFilterTag  "Filter/Analyze"
#define magick_number_threads(source,destination,chunk,multithreaded) \
  num_threads(GetMagickNumberThreads(source,destination,chunk,multithreaded))

  char
    text[MagickPathExtent];

  Image
    *image;

  MagickBooleanType
    status;

  MagickOffsetType
    progress;

  assert(images != (Image **) NULL);
  assert(*images != (Image *) NULL);
  assert((*images)->signature == MagickCoreSignature);
  (void) argc;
  (void) argv;
  status=MagickTrue;
  progress=0;
  for (image=(*images); image != (Image *) NULL; image=GetNextImageInList(image))
  {
    CacheView
      *image_view;

    double
      area;

    ssize_t
      y;

    StatisticsInfo
      brightness,
      saturation;

    if (status == MagickFalse)
      continue;
    (void) memset(&brightness,0,sizeof(brightness));
    (void) memset(&saturation,0,sizeof(saturation));
    status=MagickTrue;
    image_view=AcquireVirtualCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static) \
    shared(progress,status,brightness,saturation) \
    magick_number_threads(image,image,image->rows,1)
#endif
    for (y=0; y < (ssize_t) image->rows; y++)
    {
      const Quantum
        *p;

      ssize_t
        i,
        x;

      StatisticsInfo
        local_brightness,
        local_saturation;

      if (status == MagickFalse)
        continue;
      p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
      if (p == (const Quantum *) NULL)
        {
          status=MagickFalse;
          continue;
        }
      (void) memset(&local_brightness,0,sizeof(local_brightness));
      (void) memset(&local_saturation,0,sizeof(local_saturation));
      for (x=0; x < (ssize_t) image->columns; x++)
      {
        double
          b,
          h,
          s;

        ConvertRGBToHSL(GetPixelRed(image,p),GetPixelGreen(image,p),
          GetPixelBlue(image,p),&h,&s,&b);
        b*=QuantumRange;
        for (i=1; i <= 4; i++)
          local_brightness.sum[i]+=pow(b,(double) i);
        s*=QuantumRange;
        for (i=1; i <= 4; i++)
          local_saturation.sum[i]+=pow(s,(double) i);
        p+=GetPixelChannels(image);
      }
#if defined(MAGICKCORE_OPENMP_SUPPORT)
      #pragma omp critical (analyzeImage)
#endif
      for (i=1; i <= 4; i++)
      {
        brightness.sum[i]+=local_brightness.sum[i];
        saturation.sum[i]+=local_saturation.sum[i];
      }
    }
    image_view=DestroyCacheView(image_view);
    area=(double) image->columns*image->rows;
    brightness.mean=brightness.sum[1]/area;
    (void) FormatLocaleString(text,MagickPathExtent,"%g",brightness.mean);
    (void) SetImageProperty(image,"filter:brightness:mean",text,exception);
    brightness.standard_deviation=sqrt(brightness.sum[2]/area-
      (brightness.sum[1]/area*brightness.sum[1]/area));
    (void) FormatLocaleString(text,MagickPathExtent,"%g",
      brightness.standard_deviation);
    (void) SetImageProperty(image,"filter:brightness:standard-deviation",text,
      exception);
    if (fabs(brightness.standard_deviation) >= MagickEpsilon)
      brightness.kurtosis=(brightness.sum[4]/area-4.0*brightness.mean*
        brightness.sum[3]/area+6.0*brightness.mean*brightness.mean*
        brightness.sum[2]/area-3.0*brightness.mean*brightness.mean*
        brightness.mean*brightness.mean)/(brightness.standard_deviation*
        brightness.standard_deviation*brightness.standard_deviation*
        brightness.standard_deviation)-3.0;
    (void) FormatLocaleString(text,MagickPathExtent,"%g",brightness.kurtosis);
    (void) SetImageProperty(image,"filter:brightness:kurtosis",text,exception);
    if (brightness.standard_deviation != 0)
      brightness.skewness=(brightness.sum[3]/area-3.0*brightness.mean*
        brightness.sum[2]/area+2.0*brightness.mean*brightness.mean*
        brightness.mean)/(brightness.standard_deviation*
        brightness.standard_deviation*brightness.standard_deviation);
    (void) FormatLocaleString(text,MagickPathExtent,"%g",brightness.skewness);
    (void) SetImageProperty(image,"filter:brightness:skewness",text,exception);
    saturation.mean=saturation.sum[1]/area;
    (void) FormatLocaleString(text,MagickPathExtent,"%g",saturation.mean);
    (void) SetImageProperty(image,"filter:saturation:mean",text,exception);
    saturation.standard_deviation=sqrt(saturation.sum[2]/area-
      (saturation.sum[1]/area*saturation.sum[1]/area));
    (void) FormatLocaleString(text,MagickPathExtent,"%g",
      saturation.standard_deviation);
    (void) SetImageProperty(image,"filter:saturation:standard-deviation",text,
      exception);
    if (fabs(saturation.standard_deviation) >= MagickEpsilon)
      saturation.kurtosis=(saturation.sum[4]/area-4.0*saturation.mean*
        saturation.sum[3]/area+6.0*saturation.mean*saturation.mean*
        saturation.sum[2]/area-3.0*saturation.mean*saturation.mean*
        saturation.mean*saturation.mean)/(saturation.standard_deviation*
        saturation.standard_deviation*saturation.standard_deviation*
        saturation.standard_deviation)-3.0;
    (void) FormatLocaleString(text,MagickPathExtent,"%g",saturation.kurtosis);
    (void) SetImageProperty(image,"filter:saturation:kurtosis",text,exception);
    if (fabs(saturation.standard_deviation) >= MagickEpsilon)
      saturation.skewness=(saturation.sum[3]/area-3.0*saturation.mean*
        saturation.sum[2]/area+2.0*saturation.mean*saturation.mean*
        saturation.mean)/(saturation.standard_deviation*
        saturation.standard_deviation*saturation.standard_deviation);
    (void) FormatLocaleString(text,MagickPathExtent,"%g",saturation.skewness);
    (void) SetImageProperty(image,"filter:saturation:skewness",text,exception);
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
      {
        MagickBooleanType
          proceed;

#if defined(MAGICKCORE_OPENMP_SUPPORT)
        #pragma omp atomic
#endif
        progress++;
        proceed=SetImageProgress(image,AnalyzeImageFilterTag,progress,
          GetImageListLength(image));
        if (proceed == MagickFalse)
          status=MagickFalse;
      }
  }
  return(MagickImageFilterSignature);
}

Özel filtreyi komut satırından çağırmak için şu komutu kullanın:

magick logo: -process \"analyze\" -verbose info:
Image: logo:
  Format: LOGO (ImageMagick Logo)
  Class: PseudoClass
  Geometry: 640x480
  ...
  filter:brightness:kurtosis: 3.97886
  filter:brightness:mean: 58901.3
  filter:brightness:skewness: -2.30827
  filter:brightness:standard-deviation: 16179.8
  filter:saturation:kurtosis: 6.59719
  filter:saturation:mean: 5321.05
  filter:saturation:skewness: 2.75679
  filter:saturation:standard-deviation: 14484.7

Kendi özel görüntü filtrenizi yazmaya başlamanıza yardımcı olmak için Magick Filter Kit'i sağlıyoruz.

Derlemeden önce, ImageMagick varsayılan sistem yolunuzda değilse PKG_CONFIG_PATH ortam değişkenini ayarlayın:

export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig 
Yorum listesi
Yükleniyor..