সি, ল্যাব রঙের স্থান এবং উন্নত দুরত্ব সহ
আমি কি বলেছিলাম যে আমার কাজ শেষ হয়েছে? আমি মিথ্যে বলেছি. আমি মনে করি আমার অন্যান্য দ্রবণে অ্যালগরিদম সেখানে আউট সেরা, কিন্তু পার্ল শুধু যথেষ্ট দ্রুত নাম্বার ক্রান্চিং কাজের জন্য নয়, তাই আমি সি আমার কাজ এখন রান reimplemented সব একটি উচ্চ গুণমানে এই পোস্টে ইমেজ, প্রতি চিত্র প্রতি প্রায় 3 মিনিটের আসল তুলনায় এবং সামান্য নিম্ন মানের (0.5% স্তর) প্রতি চিত্র 20-30 সেকেন্ডে চলে। মূলত সমস্ত কাজ ইমেজম্যাগিকের সাথে সম্পন্ন হয় এবং ইমেজম্যাগিকের কিউবিক স্প্লাইন ইন্টারপোলেশন ব্যবহার করে দুরত্বটি করা হয় যা একটি ভাল / কম প্যাটার্নযুক্ত ফলাফল দেয়।
কোড
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <wand/MagickWand.h>
#define ThrowWandException(wand) \
{ \
char \
*description; \
\
ExceptionType \
severity; \
\
description=MagickGetException(wand,&severity); \
(void) fprintf(stderr,"%s %s %lu %s\n",GetMagickModule(),description); \
description=(char *) MagickRelinquishMemory(description); \
abort(); \
exit(-1); \
}
int width, height; /* Target image size */
MagickWand *source_wand, *target_wand, *img_wand, *target_lab_wand, *img_lab_wand;
PixelPacket *source_pixels, *target_pixels, *img_pixels, *target_lab_pixels, *img_lab_pixels;
Image *img, *img_lab, *target, *target_lab;
CacheView *img_lab_view, *target_lab_view;
ExceptionInfo *e;
MagickWand *load_image(const char *filename) {
MagickWand *img = NewMagickWand();
if (!MagickReadImage(img, filename)) {
ThrowWandException(img);
}
return img;
}
PixelPacket *get_pixels(MagickWand *wand) {
PixelPacket *ret = GetAuthenticPixels(
GetImageFromMagickWand(wand), 0, 0,
MagickGetImageWidth(wand), MagickGetImageHeight(wand), e);
CatchException(e);
return ret;
}
void sync_pixels(MagickWand *wand) {
SyncAuthenticPixels(GetImageFromMagickWand(wand), e);
CatchException(e);
}
MagickWand *transfer_pixels() {
if (MagickGetImageWidth(source_wand) * MagickGetImageHeight(source_wand)
!= MagickGetImageWidth(target_wand) * MagickGetImageHeight(target_wand)) {
perror("size mismtch");
}
MagickWand *img_wand = CloneMagickWand(target_wand);
img_pixels = get_pixels(img_wand);
memcpy(img_pixels, source_pixels,
MagickGetImageWidth(img_wand) * MagickGetImageHeight(img_wand) * sizeof(PixelPacket));
sync_pixels(img_wand);
return img_wand;
}
MagickWand *image_to_lab(MagickWand *img) {
MagickWand *lab = CloneMagickWand(img);
TransformImageColorspace(GetImageFromMagickWand(lab), LabColorspace);
return lab;
}
int lab_distance(PixelPacket *a, PixelPacket *b) {
int l_diff = (GetPixelL(a) - GetPixelL(b)) / 256,
a_diff = (GetPixela(a) - GetPixela(b)) / 256,
b_diff = (GetPixelb(a) - GetPixelb(b)) / 256;
return (l_diff * l_diff + a_diff * a_diff + b_diff * b_diff);
}
int should_swap(int x1, int x2, int y1, int y2) {
int dist = lab_distance(&img_lab_pixels[width * y1 + x1], &target_lab_pixels[width * y1 + x1])
+ lab_distance(&img_lab_pixels[width * y2 + x2], &target_lab_pixels[width * y2 + x2]);
int swapped_dist = lab_distance(&img_lab_pixels[width * y2 + x2], &target_lab_pixels[width * y1 + x1])
+ lab_distance(&img_lab_pixels[width * y1 + x1], &target_lab_pixels[width * y2 + x2]);
return swapped_dist < dist;
}
void pixel_multiply_add(MagickPixelPacket *dest, PixelPacket *src, double mult) {
dest->red += (double)GetPixelRed(src) * mult;
dest->green += ((double)GetPixelGreen(src) - 32768) * mult;
dest->blue += ((double)GetPixelBlue(src) - 32768) * mult;
}
#define min(x,y) (((x) < (y)) ? (x) : (y))
#define max(x,y) (((x) > (y)) ? (x) : (y))
double mpp_distance(MagickPixelPacket *a, MagickPixelPacket *b) {
double l_diff = QuantumScale * (a->red - b->red),
a_diff = QuantumScale * (a->green - b->green),
b_diff = QuantumScale * (a->blue - b->blue);
return (l_diff * l_diff + a_diff * a_diff + b_diff * b_diff);
}
void do_swap(PixelPacket *pix, int x1, int x2, int y1, int y2) {
PixelPacket tmp = pix[width * y1 + x1];
pix[width * y1 + x1] = pix[width * y2 + x2];
pix[width * y2 + x2] = tmp;
}
int should_swap_dither(double detail, int x1, int x2, int y1, int y2) {
// const InterpolatePixelMethod method = Average9InterpolatePixel;
const InterpolatePixelMethod method = SplineInterpolatePixel;
MagickPixelPacket img1, img2, img1s, img2s, target1, target2;
GetMagickPixelPacket(img, &img1);
GetMagickPixelPacket(img, &img2);
GetMagickPixelPacket(img, &img1s);
GetMagickPixelPacket(img, &img2s);
GetMagickPixelPacket(target, &target1);
GetMagickPixelPacket(target, &target2);
InterpolateMagickPixelPacket(img, img_lab_view, method, x1, y1, &img1, e);
InterpolateMagickPixelPacket(img, img_lab_view, method, x2, y2, &img2, e);
InterpolateMagickPixelPacket(target, target_lab_view, method, x1, y1, &target1, e);
InterpolateMagickPixelPacket(target, target_lab_view, method, x2, y2, &target2, e);
do_swap(img_lab_pixels, x1, x2, y1, y2);
// sync_pixels(img_wand);
InterpolateMagickPixelPacket(img, img_lab_view, method, x1, y1, &img1s, e);
InterpolateMagickPixelPacket(img, img_lab_view, method, x2, y2, &img2s, e);
do_swap(img_lab_pixels, x1, x2, y1, y2);
// sync_pixels(img_wand);
pixel_multiply_add(&img1, &img_lab_pixels[width * y1 + x1], detail);
pixel_multiply_add(&img2, &img_lab_pixels[width * y2 + x2], detail);
pixel_multiply_add(&img1s, &img_lab_pixels[width * y2 + x2], detail);
pixel_multiply_add(&img2s, &img_lab_pixels[width * y1 + x1], detail);
pixel_multiply_add(&target1, &target_lab_pixels[width * y1 + x1], detail);
pixel_multiply_add(&target2, &target_lab_pixels[width * y2 + x2], detail);
double dist = mpp_distance(&img1, &target1)
+ mpp_distance(&img2, &target2);
double swapped_dist = mpp_distance(&img1s, &target1)
+ mpp_distance(&img2s, &target2);
return swapped_dist + 1.0e-4 < dist;
}
int main(int argc, char *argv[]) {
if (argc != 7) {
fprintf(stderr, "Usage: %s source.png target.png dest nodither_pct dither_pct detail\n", argv[0]);
return 1;
}
char *source_filename = argv[1];
char *target_filename = argv[2];
char *dest = argv[3];
double nodither_pct = atof(argv[4]);
double dither_pct = atof(argv[5]);
double detail = atof(argv[6]) - 1;
const int SWAPS_PER_LOOP = 1000000;
int nodither_limit = ceil(SWAPS_PER_LOOP * nodither_pct / 100);
int dither_limit = ceil(SWAPS_PER_LOOP * dither_pct / 100);
int dither = 0, frame = 0;
char outfile[256], cmdline[1024];
sprintf(outfile, "out/%s.png", dest);
MagickWandGenesis();
e = AcquireExceptionInfo();
source_wand = load_image(source_filename);
source_pixels = get_pixels(source_wand);
target_wand = load_image(target_filename);
target_pixels = get_pixels(target_wand);
img_wand = transfer_pixels();
img_pixels = get_pixels(img_wand);
target_lab_wand = image_to_lab(target_wand);
target_lab_pixels = get_pixels(target_lab_wand);
img_lab_wand = image_to_lab(img_wand);
img_lab_pixels = get_pixels(img_lab_wand);
img = GetImageFromMagickWand(img_lab_wand);
target = GetImageFromMagickWand(target_lab_wand);
img_lab_view = AcquireAuthenticCacheView(img, e);
target_lab_view = AcquireAuthenticCacheView(target,e);
CatchException(e);
width = MagickGetImageWidth(img_wand);
height = MagickGetImageHeight(img_wand);
while (1) {
int swaps_made = 0;
for (int n = 0 ; n < SWAPS_PER_LOOP ; n++) {
int x1 = rand() % width,
x2 = rand() % width,
y1 = rand() % height,
y2 = rand() % height;
int swap = dither ?
should_swap_dither(detail, x1, x2, y1, y2)
: should_swap(x1, x2, y1, y2);
if (swap) {
do_swap(img_pixels, x1, x2, y1, y2);
do_swap(img_lab_pixels, x1, x2, y1, y2);
swaps_made ++;
}
}
sync_pixels(img_wand);
if (!MagickWriteImages(img_wand, outfile, MagickTrue)) {
ThrowWandException(img_wand);
}
img_pixels = get_pixels(img_wand);
sprintf(cmdline, "cp out/%s.png anim/%s/%05i.png", dest, dest, frame++);
system(cmdline);
if (!dither && swaps_made < nodither_limit) {
sprintf(cmdline, "cp out/%s.png out/%s-nodither.png", dest, dest);
system(cmdline);
dither = 1;
} else if (dither && swaps_made < dither_limit)
break;
}
return 0;
}
সঙ্গে সংকলন
gcc -std=gnu99 -O3 -march=native -ffast-math \
-o transfer `pkg-config --cflags MagickWand` \
transfer.c `pkg-config --libs MagickWand` -lm
ফলাফল
বেশিরভাগ পার্ল সংস্করণ হিসাবে একই, কিছুটা ভাল, তবে কিছু ব্যতিক্রম আছে। ডাইটিং সাধারণত সাধারণভাবে কম লক্ষণীয়। চিৎকার -> স্টারি নাইটে "জ্বলন্ত পর্বত" প্রভাব নেই, এবং কামারো ধূসর পিক্সেলের সাথে কম চটকদার দেখাচ্ছে looks আমি মনে করি পার্ল সংস্করণের কালারস্পেস কোডটিতে লো-স্যাচুরেশন পিক্সেল সহ একটি বাগ রয়েছে।
আমেরিকান গথিক প্যালেট
মোনা লিসা প্যালেট
স্টারি নাইট প্যালেট
চিৎকার প্যালেট
গোলক প্যালেট
মুস্তং (কামারো প্যালেট)
কামারো (মুস্তং প্যালেট)