Конкурс Популярности — Назначьте Среднее Значение Изображению

  • Автор темы Armaggedon
  • Обновлено
  • 22, Oct 2024
  • #1

Напишите программу, которая принимает стандартный настоящий цвет изображение и один 24-битный RGB цвет (три цифры от 0 до 255). Измените входное изображение (или выведите новое изображение с теми же размерами) так, чтобы его средний цвет это именно тот цвет, который был введен. Для достижения этой цели вы можете изменить пиксели входного изображения любым удобным для вас способом. но цель состоит в том, чтобы сделать изменения цвета максимально незаметными визуально..

средний цвет изображения RGB на самом деле представляет собой набор из трех арифметические средства, по одному на каждый цветовой канал. Среднее значение красного — это сумма значений красного для всех пикселей изображения, деленная на общее количество пикселей (площадь изображения), округленная до ближайшего целого числа. Зеленые и синие средние значения рассчитываются таким же образом.

Этот Питон 2ПИЛ) скрипт может вычислять средний цвет большинства форматов файлов изображений:

 from PIL import Image
print 'Enter image file'
im = Image.open(raw_input()).convert('RGB')
pixels = im.load()
avg = [0, 0, 0]
for x in range(im.size[0]):

for y in range(im.size[1]):

for i in range(3):

avg[i] += pixels[x, y][i]
print 'The average color is', tuple(c // (im.size[0] * im.size[1]) for c in avg)
 

(Есть аналогичные средние цветовые программы здесь, но они не обязательно выполняют одинаковые вычисления.)

Основным требованием к вашей программе является то, что для любой входное изображение, средний цвет соответствующего выходного изображения должен точно соответствовать введенному цвету - согласно фрагменту Python или некоторому эквивалентному коду. Выходное изображение также должно иметь те же размеры, что и входное изображение.

Таким образом, технически вы могли бы представить программу, которая просто окрашивает весь ввод в указанный средний цвет (потому что среднее значение всегда будет этим цветом), но это конкурс популярности - победителем станет работа, набравшая наибольшее количество голосов., и такая тривиальная заявка не принесет вам много голосов. Новые идеи, такие как использование особенностей человеческого зрения или уменьшение изображения и рисование вокруг него цветной рамки (надеюсь), принесут вам голоса.

Обратите внимание, что определенные комбинации средних цветов и изображений требуют чрезвычайно заметных изменений цвета. Например, если средний цвет для сопоставления был черным (0, 0, 0), любой входное изображение необходимо будет сделать полностью черным, потому что, если бы какие-либо пиксели имели ненулевые значения, они также сделали бы среднее значение ненулевым (за исключением ошибок округления). Помните об этих ограничениях при голосовании.

Тестовые изображения

Некоторые изображения и их средние цвета по умолчанию, с которыми можно поиграть. Нажмите, чтобы увидеть полные размеры.

А. средний (127, 127, 127)

конкурс популярности — назначьте среднее значение изображению От фейжежоко's Изображения со всеми цветами отвечать. Найденный оригинальный на его блог.

Б. средний (62, 71, 73)

конкурс популярности — назначьте среднее значение изображению Иокогама. Предоставлено Геобиты.

С. средний (115, 112, 111)

конкурс популярности — назначьте среднее значение изображению Токио. Предоставлено Геобиты.

Д. средний (154, 151, 154)

конкурс популярности — назначьте среднее значение изображению Эшера Водопад. Оригинал.

Э. средний (105, 103, 102)

конкурс популярности — назначьте среднее значение изображению Гора Шаста. Предоставлено мной.

Ф. средний (75, 91, 110)

конкурс популярности — назначьте среднее значение изображению Звездная ночь

Примечания

  • Точные форматы ввода и вывода, а также типы файлов изображений, которые использует ваша программа, не имеют большого значения. Просто убедитесь, что вам понятно, как использовать вашу программу.
  • Вероятно, хорошей идеей (но не техническим требованием) является то, что если изображение уже имеет целевой средний цвет, его следует выводить как есть.
  • Пожалуйста, опубликуйте тестовые изображения со средним значением цвета (150, 100, 100) или (75, 91, 110), чтобы избиратели могли видеть одни и те же входные данные в разных решениях. (Публикация большего количества примеров — это нормально, даже приветствуется.)

#конкурс популярности #графический вывод #обработка изображений

Armaggedon


Рег
03 Feb, 2010

Тем
81

Постов
187

Баллов
622
  • 26, Oct 2024
  • #2

Python 2 + PIL, простое масштабирование цвета

 
 
 
 import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.*;

import javax.imageio.ImageIO;

public class Averager {

static Random r;

static long sigmaR=0,sigmaG=0,sigmaB=0;

static int w,h;

static int rbar,gbar,bbar;

static BufferedImage i;

private static File file;

static void upRed(){

int x=r.nextInt(w);

int y=r.nextInt(h);

Color c=new Color(i.getRGB(x, y));

if(c.getRed()==255)return;

sigmaR++;

c=new Color(c.getRed()+1,c.getGreen(),c.getBlue());

i.setRGB(x, y,c.getRGB());

}

static void downRed(){

int x=r.nextInt(w);

int y=r.nextInt(h);

Color c=new Color(i.getRGB(x, y));

if(c.getRed()==0)return;

sigmaR--;

c=new Color(c.getRed()-1,c.getGreen(),c.getBlue());

i.setRGB(x, y,c.getRGB());

}

static void upGreen(){

int x=r.nextInt(w);

int y=r.nextInt(h);

Color c=new Color(i.getRGB(x, y));

if(c.getGreen()==255)return;

sigmaG++;

c=new Color(c.getRed(),c.getGreen()+1,c.getBlue());

i.setRGB(x, y,c.getRGB());

}

static void downGreen(){

int x=r.nextInt(w);

int y=r.nextInt(h);

Color c=new Color(i.getRGB(x, y));

if(c.getGreen()==0)return;

sigmaG--;

c=new Color(c.getRed(),c.getGreen()-1,c.getBlue());

i.setRGB(x,y,c.getRGB());

}

static void upBlue(){

int x=r.nextInt(w);

int y=r.nextInt(h);

Color c=new Color(i.getRGB(x, y));

if(c.getBlue()==255)return;

sigmaB++;

c=new Color(c.getRed(),c.getGreen(),c.getBlue()+1);

i.setRGB(x, y,c.getRGB());

}

static void downBlue(){

int x=r.nextInt(w);

int y=r.nextInt(h);

Color c=new Color(i.getRGB(x, y));

if(c.getBlue()==0)return;

sigmaB--;

c=new Color(c.getRed(),c.getGreen(),c.getBlue()-1);

i.setRGB(x,y,c.getRGB());

}

public static void main(String[]a) throws Exception{

Scanner in=new Scanner(System.in);

i=ImageIO.read(file=new File(in.nextLine()));

rbar=in.nextInt();

gbar=in.nextInt();

bbar=in.nextInt();

w=i.getWidth();

h=i.getHeight();

final int npix=w*h;

r=new Random(npix*(long)i.hashCode());

for(int x=0;x<w;x++){

for(int y=0;y<h;y++){

Color c=new Color(i.getRGB(x, y));

sigmaR+=c.getRed();

sigmaG+=c.getGreen();

sigmaB+=c.getBlue();

}

}

while(sigmaR/npix<rbar){

upRed();

}

while(sigmaR/npix>rbar){

downRed();

}

while(sigmaG/npix<gbar){

upGreen();

}

while(sigmaG/npix>gbar){

downGreen();

}

while(sigmaB/npix<bbar){

upBlue();

}

while(sigmaB/npix>bbar){

downBlue();

}

String k=file.getName().split("\\.")[0];

ImageIO.write(i,"png",new File(k="out_"+k+".png"));

}
}
 

Вот наивный подход, который должен послужить хорошей основой. На каждой итерации мы сравниваем наше текущее среднее значение с желаемым средним значением и масштабируем RGB каждого пикселя в соответствующем соотношении. Однако нам следует быть немного осторожными по двум причинам:

  • Масштабирование 0 по-прежнему приводит к 0, поэтому перед масштабированием мы добавляем что-то маленькое (здесь 0 )

  • Значения RGB находятся в диапазоне от 0 до 255, поэтому нам нужно соответствующим образом отрегулировать соотношение, чтобы компенсировать тот факт, что масштабирование ограниченных пикселей ничего не дает.

Изображения сохраняются в формате PNG, поскольку сохранение в формате JPG искажает средние значения цвета.

Пример вывода

(40, 40, 40)

(150, 100, 100)

(75, 91, 110), палитра «Звездная ночь»

 

Natteri


Рег
03 Jan, 2007

Тем
72

Постов
212

Баллов
592
  • 26, Oct 2024
  • #3

C++, гамма-коррекция

При этом выполняется регулировка яркости изображения с использованием простой гамма-коррекции, при этом значение гаммы определяется отдельно для каждого компонента, чтобы соответствовать целевому среднему значению.

Шаги высокого уровня:

  1. Считайте изображение и извлеките гистограмму для каждого компонента цвета.
  2. Выполните двоичный поиск значения гаммы для каждого компонента. Бинарный поиск выполняется по значениям гаммы до тех пор, пока результирующая гистограмма не будет иметь желаемое среднее значение.
  3. Прочтите изображение второй раз и примените гамма-коррекцию.

Весь ввод/вывод изображений использует файлы PPM в формате ASCII. Изображения были конвертированы из/в PNG с помощью GIMP. Код запускался на Mac, преобразование изображений выполнялось в Windows.

Код:

255

Сам код довольно прост. Одна тонкая, но важная деталь заключается в том, что, хотя значения цветов находятся в диапазоне [0, 255], я сопоставляю их с гамма-кривой, как если бы диапазон был [-1, 256]. Это позволяет принудительно установить среднее значение на 0 или 255. В противном случае 0 всегда останется 0, а 255 всегда останется 255, что никогда не позволит получить среднее значение 0/255.

Чтобы использовать:

  1. Сохраните код в файл с расширением 1 , e.g. 0 .
  2. Скомпилировать с 255 .
  3. Беги с from PIL import Image import random import math SOURCE = 'input.png' OUTPUT = 'output.png' AVERAGE = [150, 100, 100] im = Image.open(SOURCE).convert('RGB') pixels = im.load() w = im.size[0] h = im.size[1] npixels = w * h maxdiff = 0.1 # for consistent results... random.seed(42) order = range(npixels) random.shuffle(order) def calc_sum(pixels, w, h): sums = [0, 0, 0] for x in range(w): for y in range(h): for i in range(3): sums[i] += pixels[x, y][i] return sums def get_coordinates(index, w): return tuple([index % w, index // w]) desired_sums = [AVERAGE[0] * npixels, AVERAGE[1] * npixels, AVERAGE[2] * npixels] sums = calc_sum(pixels, w, h) for i in range(3): while sums[i] != desired_sums[i]: for j in range(npixels): if sums[i] == desired_sums[i]: break elif sums[i] < desired_sums[i]: coord = get_coordinates(order[j], w) pixel = list(pixels[coord]) delta = int(maxdiff * (255 - pixel[i])) if delta == 0 and pixel[i] != 255: delta = 1 delta = min(delta, desired_sums[i] - sums[i]) sums[i] += delta pixel[i] += delta pixels[coord] = tuple(pixel) else: coord = get_coordinates(order[j], w) pixel = list(pixels[coord]) delta = int(maxdiff * pixel[i]) if delta == 0 and pixel[i] != 0: delta = 1 delta = min(delta, sums[i] - desired_sums[i]) sums[i] -= delta pixel[i] -= delta pixels[coord] = tuple(pixel) # output image for x in range(w): for y in range(h): im.putpixel(tuple([x, y]), pixels[tuple([x, y])]) im.save(OUTPUT) .

Пример вывода для 40, 40, 40

Обратите внимание, что изображения для всех более крупных образцов включены в формате JPEG, поскольку они превышают ограничение размера SE для PNG. Поскольку JPEG — это формат сжатия с потерями, они могут не совсем соответствовать целевому среднему значению. У меня есть PNG-версия всех файлов, которая точно соответствует.

Пример вывода для 150, 100, 100:

Пример вывода для 75, 91, 110:

 

Stateless


Рег
25 Sep, 2011

Тем
60

Постов
195

Баллов
525
  • 26, Oct 2024
  • #4

Питон 2 + ПИЛ

./force input.ppm targetR targetG target >output.ppm

Это перебирает каждый пиксель в случайном порядке и уменьшает расстояние между каждым компонентом цвета пикселя и c++ -o force -O2 force.cpp or force.cpp (в зависимости от того, меньше или больше текущее среднее желаемое). Расстояние уменьшается на фиксированный мультипликативный коэффициент. Это повторяется до тех пор, пока не будет получено желаемое среднее значение. Сокращение всегда не менее .cpp , unless the color is #include <cmath> #include <string> #include <vector> #include <sstream> #include <fstream> #include <iostream> static inline int mapVal(int val, float gamma) { float relVal = (val + 1.0f) / 257.0f; float newRelVal = powf(relVal, gamma); int newVal = static_cast<int>(newRelVal * 257.0f - 0.5f); if (newVal < 0) { newVal = 0; } else if (newVal > 255) { newVal = 255; } return newVal; } struct Histogram { Histogram(); bool read(const std::string fileName); int getAvg(int colIdx) const; void adjust(const Histogram& origHist, int colIdx, float gamma); int pixCount; std::vector<int> freqA[3]; }; Histogram::Histogram() : pixCount(0) { for (int iCol = 0; iCol < 3; ++iCol) { freqA[iCol].resize(256, 0); } } bool Histogram::read(const std::string fileName) { for (int iCol = 0; iCol < 3; ++iCol) { freqA[iCol].assign(256, 0); } std::ifstream inStrm(fileName); std::string format; inStrm >> format; if (format != "P3") { std::cerr << "invalid PPM header" << std::endl; return false; } int w = 0, h = 0; inStrm >> w >> h; if (w <= 0 || h <= 0) { std::cerr << "invalid size" << std::endl; return false; } int maxVal = 0; inStrm >> maxVal; if (maxVal != 255) { std::cerr << "invalid max value (255 expected)" << std::endl; return false; } pixCount = w * h; int sumR = 0, sumG = 0, sumB = 0; for (int iPix = 0; iPix < pixCount; ++iPix) { int r = 0, g = 0, b = 0; inStrm >> r >> g >> b; ++freqA[0][r]; ++freqA[1][g]; ++freqA[2][b]; } return true; } int Histogram::getAvg(int colIdx) const { int avg = 0; for (int val = 0; val < 256; ++val) { avg += freqA[colIdx][val] * val; } return avg / pixCount; } void Histogram::adjust(const Histogram& origHist, int colIdx, float gamma) { freqA[colIdx].assign(256, 0); for (int val = 0; val < 256; ++val) { int newVal = mapVal(val, gamma); freqA[colIdx][newVal] += origHist.freqA[colIdx][val]; } } void mapImage(const std::string fileName, float gammaA[]) { std::ifstream inStrm(fileName); std::string format; inStrm >> format; int w = 0, h = 0; inStrm >> w >> h; int maxVal = 0; inStrm >> maxVal; std::cout << "P3" << std::endl; std::cout << w << " " << h << std::endl; std::cout << "255" << std::endl; int nPix = w * h; for (int iPix = 0; iPix < nPix; ++iPix) { int inRgb[3] = {0}; inStrm >> inRgb[0] >> inRgb[1] >> inRgb[2]; int outRgb[3] = {0}; for (int iCol = 0; iCol < 3; ++iCol) { outRgb[iCol] = mapVal(inRgb[iCol], gammaA[iCol]); } std::cout << outRgb[0] << " " << outRgb[1] << " " << outRgb[2] << std::endl; } } int main(int argc, char* argv[]) { if (argc < 5) { std::cerr << "usage: " << argv[0] << " ppmFileName targetR targetG targetB" << std::endl; return 1; } std::string inFileName = argv[1]; int targAvg[3] = {0}; std::istringstream strmR(argv[2]); strmR >> targAvg[0]; std::istringstream strmG(argv[3]); strmG >> targAvg[1]; std::istringstream strmB(argv[4]); strmB >> targAvg[2]; Histogram origHist; if (!origHist.read(inFileName)) { return 1; } Histogram newHist(origHist); float gammaA[3] = {0.0f}; for (int iCol = 0; iCol < 3; ++iCol) { float minGamma = 0.0f; float maxGamma = 1.0f; for (;;) { newHist.adjust(origHist, iCol, maxGamma); int avg = newHist.getAvg(iCol); if (avg <= targAvg[iCol]) { break; } maxGamma *= 2.0f; } for (;;) { float midGamma = 0.5f * (minGamma + maxGamma); newHist.adjust(origHist, iCol, midGamma); int avg = newHist.getAvg(iCol); if (avg < targAvg[iCol]) { maxGamma = midGamma; } else if (avg > targAvg[iCol]) { minGamma = midGamma; } else { gammaA[iCol] = midGamma; break; } } } mapImage(inFileName, gammaA); return 0; } (или 0.01 ), to ensure that the processing does not stall once the pixel is close to white or black.

Пример вывода

(40, 40, 40)

(150, 100, 100)

(75, 91, 110)

 

1233333333


Рег
28 Jul, 2011

Тем
59

Постов
184

Баллов
509
  • 26, Oct 2024
  • #5

Ява

Подход, основанный на ГСЧ. Немного медленно для больших входных изображений.

from PIL import Image import math INFILE = "street.jpg" OUTFILE = "output.png" AVERAGE = (150, 100, 100) im = Image.open(INFILE) im = im.convert("RGB") width, height = prev_size = im.size pixels = {(x, y): list(im.getpixel((x, y))) for x in range(width) for y in range(height)} def get_avg(): total_rgb = [0, 0, 0] for x in range(width): for y in range(height): for i in range(3): total_rgb[i] += int(pixels[x, y][i]) return [float(x)/(width*height) for x in total_rgb] curr_avg = get_avg() while tuple(int(x) for x in curr_avg) != AVERAGE: print curr_avg non_capped = [0, 0, 0] total_rgb = [0, 0, 0] for x in range(width): for y in range(height): for i in range(3): if curr_avg[i] < AVERAGE[i] and pixels[x, y][i] < 255: non_capped[i] += 1 total_rgb[i] += int(pixels[x, y][i]) elif curr_avg[i] > AVERAGE[i] and pixels[x, y][i] > 0: non_capped[i] += 1 total_rgb[i] += int(pixels[x, y][i]) ratios = [1 if z == 0 else x/(y/float(z)) for x,y,z in zip(AVERAGE, total_rgb, non_capped)] for x in range(width): for y in range(height): col = [] for i in range(3): new_col = (pixels[x, y][i] + 0.01) * ratios[i] col.append(min(255, max(0, new_col))) pixels[x, y] = tuple(col) curr_avg = get_avg() print curr_avg for pixel in pixels: im.putpixel(pixel, tuple(int(x) for x in pixels[pixel])) im.save(OUTFILE)

Тесты:

(40,40,40)

(150,100,100)

(75,91,110)

 

Cvs


Рег
22 Jun, 2006

Тем
66

Постов
194

Баллов
524
Тем
403,760
Комментарии
400,028
Опыт
2,418,908

Интересно