Для каждого пикселя вы должны преобразовать цвет в оттенки серого - что-то вроде
$ серый = $ красный * 0,299 + $ зеленый * 0,587 + $ синий * 0,114;
(это весовые коэффициенты NTSC; существуют другие подобные весовые коэффициенты. Это имитирует различную чувствительность глаза к разным цветам).
Тогда вам нужно выбрать значение отсечения - обычно половину максимального значения пикселя, но в зависимости от изображения вы можете предпочесть более высокое значение (сделать изображение темнее) или ниже (сделать изображение ярче).
Простое сравнение каждого пикселя с отсечкой приводит к потере большого количества деталей, т. Е. Большие темные области становятся полностью черными, поэтому для сохранения дополнительной информации можно выполнить дизеринг. Как правило, начинайте с левого верхнего угла изображения: для каждого пикселя добавьте ошибку (разницу между исходным значением и конечным назначенным значением) для пикселей слева и выше перед сравнением со значением отсечения.
Имейте в виду, что выполнение этого в PHP будет очень медленным - вы намного дальше найдете библиотеку, которая обеспечивает это.