From 56da5e3fb1579bdb973582516fd7eff7bbfcfe0c Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Tue, 31 May 2016 23:06:06 -0400 Subject: Initial commit --- sap.cpp | 473 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 sap.cpp (limited to 'sap.cpp') diff --git a/sap.cpp b/sap.cpp new file mode 100644 index 0000000..4e2f66c --- /dev/null +++ b/sap.cpp @@ -0,0 +1,473 @@ +extern "C" { +#include +#include +#include +#include +} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +Container split(std::string input, std::string delimiter) +{ + Container result; + + while (!input.empty()) + { + int divider = input.find(delimiter); + if (divider == std::string::npos) + { + result.push_back(input); + + input = ""; + } else { + result.push_back(input.substr(0, divider)); + + input = input.substr(divider+delimiter.length()); + } + } + + return result; +} + +template +std::string implode(InputIterator first, InputIterator last, std::string delimiter) +{ + std::stringstream result; + + for (InputIterator it = first; it != last; it++) + { + if (it != first) + { + result << delimiter; + } + + result << *it; + } + + return result.str(); +} + +int maxWordsInLine(std::vector words, Magick::Image& textimage) +{ + int result = 0; + + std::string curline = ""; + Magick::TypeMetric metric; + for (auto word : words) + { + curline += " " + word; + + textimage.fontTypeMetrics(curline, &metric); + if (metric.textWidth() > ((textimage.columns()/10)*9)) + { + break; + } else { + result++; + } + } + + return result; +} + +int minHeightRequired(std::vector words, Magick::Image& textimage) +{ + int result = 0; + while (!words.empty()) + { + int prefixlen = maxWordsInLine(words, textimage); + std::string prefixText = implode(std::begin(words), std::begin(words) + prefixlen, " "); + std::vector suffix(std::begin(words) + prefixlen, std::end(words)); + Magick::TypeMetric metric; + textimage.fontTypeMetrics(prefixText, &metric); + result += metric.textHeight() + 5; + + words = suffix; + } + + return result - 5; +} + +void layoutText(Magick::Image& textimage, Magick::Image& shadowimage, int width, int height, std::string text) +{ + DIR* fontdir; + struct dirent* ent; + if ((fontdir = opendir("fonts")) == nullptr) + { + std::cout << "Couldn't find fonts." << std::endl; + return; + } + + std::vector fonts; + while ((ent = readdir(fontdir)) != nullptr) + { + std::string dname(ent->d_name); + if ((dname.find(".otf") != std::string::npos) || (dname.find(".ttf") != std::string::npos)) + { + fonts.push_back(dname); + } + } + + closedir(fontdir); + + textimage.fillColor(Magick::Color(MaxRGB, MaxRGB, MaxRGB, MaxRGB * 0.0)); + shadowimage.fillColor(Magick::Color(0, 0, 0, 0)); + shadowimage.strokeColor("black"); + + int minSize = 48; + int realMaxSize = 96; + int maxSize = realMaxSize; + Magick::TypeMetric metric; + std::string font; + auto words = split>(text, " "); + int top = 5; + int minWords = 1; + while (!words.empty()) + { + if (font.empty() || (rand() % 10 == 0)) + { + font = fonts[rand() % fonts.size()]; + textimage.font("fonts/" + font); + shadowimage.font("fonts/" + font); + } + + int size = rand() % (maxSize - minSize + 1) + minSize; + textimage.fontPointsize(size); + int maxWords = maxWordsInLine(words, textimage); + int touse; + if (minWords > maxWords) + { + touse = maxWords; + } else { + touse = rand() % (maxWords - minWords + 1) + minWords; + } + std::string prefixText = implode(std::begin(words), std::begin(words) + touse, " "); + std::vector suffix(std::begin(words) + touse, std::end(words)); + textimage.fontTypeMetrics(prefixText, &metric); + + textimage.fontPointsize(minSize); + int lowpadding = minHeightRequired(suffix, textimage); + int freespace = height - 5 - top - lowpadding - metric.textHeight(); + std::cout << "top of " << top << " with lowpad of " << lowpadding << " and textheight of " << metric.textHeight() << " with freespace=" << freespace << std::endl; + if (freespace < 0) + { + minWords = touse; + + continue; + } + + maxSize = realMaxSize; + minWords = 1; + + int toppadding; + if (rand() % 2 == 0) + { + // Exponential distribution, biased toward top + toppadding = log(rand() % (int)exp(freespace + 1) + 1); + } else { + // Linear distribution, biased toward bottom + toppadding = rand() % (freespace + 1); + } + + int leftx = rand() % (width - 10 - (int)metric.textWidth()) + 5; + std::cout << "printing at " << leftx << "," << (top + toppadding + metric.ascent()) << std::endl; + textimage.fontPointsize(size); + textimage.annotate(prefixText, Magick::Geometry(0, 0, leftx, top + toppadding + metric.ascent())); + + shadowimage.fontPointsize(size); + shadowimage.strokeWidth(size / 10); + shadowimage.annotate(prefixText, Magick::Geometry(0, 0, leftx, top + toppadding + metric.ascent())); + //shadowimage.draw(Magick::DrawableRectangle(leftx - 5, top + toppadding, leftx + metric.textWidth() + 5, top + toppadding + metric.textHeight() + 10 + metric.descent())); + + words = suffix; + top += toppadding + metric.textHeight(); + } + + Magick::PixelPacket* shadowpixels = shadowimage.getPixels(0, 0, width, height); + Magick::PixelPacket* textpixels = textimage.getPixels(0, 0, width, height); + for (int j=0; jstreams[stream_index]; + + // find decoder for the stream + dec_ctx = st->codec; + dec = avcodec_find_decoder(dec_ctx->codec_id); + if (!dec) + { + fprintf(stderr, "Failed to find %s codec\n", av_get_media_type_string(type)); + return AVERROR(EINVAL); + } + + // Init the decoders, with or without reference counting + av_dict_set(&opts, "refcounted_frames", "0", 0); + if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) + { + fprintf(stderr, "Failed to open %s codec\n", av_get_media_type_string(type)); + return ret; + } + + *stream_idx = stream_index; + } + + return 0; +} + +int main(int argc, char** argv) +{ + srand(time(NULL)); + rand(); rand(); rand(); rand(); + + Magick::InitializeMagick(nullptr); + av_register_all(); + + YAML::Node config = YAML::LoadFile("config.yml"); + + twitter::auth auth; + auth.setConsumerKey(config["consumer_key"].as()); + auth.setConsumerSecret(config["consumer_secret"].as()); + auth.setAccessKey(config["access_key"].as()); + auth.setAccessSecret(config["access_secret"].as()); + + twitter::client client(auth); + + std::ifstream infile("corpus1.txt"); + std::string corpus; + std::string line; + while (getline(infile, line)) + { + if (line.back() == '\r') + { + line.pop_back(); + } + + corpus += line + " "; + } + + infile.close(); + + std::ifstream infile2("corpus2.txt"); + std::string corpus2; + while (getline(infile2, line)) + { + if (line.back() == '\r') + { + line.pop_back(); + } + + corpus2 += line + " "; + } + + infile2.close(); + + rawr kgramstats; + kgramstats.addCorpus(corpus); + kgramstats.addCorpus(corpus2); + kgramstats.compile(5); + kgramstats.setMinCorpora(2); + + DIR* videodir; + struct dirent* ent; + if ((videodir = opendir("videos")) == nullptr) + { + std::cout << "Couldn't find videos." << std::endl; + return -1; + } + + std::vector videos; + while ((ent = readdir(videodir)) != nullptr) + { + std::string dname(ent->d_name); + if (dname.find(".mp4") != std::string::npos) + { + videos.push_back(dname); + } + } + + closedir(videodir); + + for (;;) + { + std::string video = "videos/" + videos[rand() % videos.size()]; + std::cout << "Opening " << video << std::endl; + + AVFormatContext* format = nullptr; + if (avformat_open_input(&format, video.c_str(), nullptr, nullptr)) + { + std::cout << "could not open file" << std::endl; + return 1; + } + + if (avformat_find_stream_info(format, nullptr)) + { + std::cout << "could not read stream" << std::endl; + return 5; + } + + int video_stream_idx = -1; + if (open_codec_context(&video_stream_idx, format, AVMEDIA_TYPE_VIDEO)) + { + std::cout << "could not open codec" << std::endl; + return 6; + } + + AVStream* stream = format->streams[video_stream_idx]; + AVCodecContext* codec = stream->codec; + int codecw = codec->width; + int codech = codec->height; + + int64_t seek = (rand() % format->duration) * codec->time_base.num / codec->time_base.den; + std::cout << seek << std::endl; + if (av_seek_frame(format, video_stream_idx, seek, 0)) + { + std::cout << "could not seek" << std::endl; + return 4; + } + + AVPacket packet; + av_init_packet(&packet); + + AVFrame* frame = av_frame_alloc(); + AVFrame* converted = av_frame_alloc(); + + int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecw, codech, 1); + uint8_t* buffer = new uint8_t[buffer_size]; + + av_image_alloc(converted->data, converted->linesize, codecw, codech, AV_PIX_FMT_RGB24, 1); + + for (;;) + { + if (av_read_frame(format, &packet)) + { + std::cout << "could not read frame" << std::endl; + return 2; + } + + if (packet.stream_index != video_stream_idx) + { + continue; + } + + int got_pic; + if (avcodec_decode_video2(codec, frame, &got_pic, &packet) < 0) + { + std::cout << "could not decode frame" << std::endl; + return 7; + } + + if (!got_pic) + { + continue; + } + + if (packet.flags && AV_PKT_FLAG_KEY) + { + SwsContext* sws = sws_getContext(codecw, codech, codec->pix_fmt, codecw, codech, AV_PIX_FMT_RGB24, 0, nullptr, nullptr, 0); + sws_scale(sws, frame->data, frame->linesize, 0, codech, converted->data, converted->linesize); + sws_freeContext(sws); + + av_image_copy_to_buffer(buffer, buffer_size, converted->data, converted->linesize, AV_PIX_FMT_RGB24, codecw, codech, 1); + av_frame_free(&frame); + av_frame_free(&converted); + av_packet_unref(&packet); + avcodec_close(codec); + avformat_close_input(&format); + + int width = 1024; + int height = codech * width / codecw; + + Magick::Image image; + image.read(codecw, codech, "RGB", Magick::CharPixel, buffer); + image.zoom(Magick::Geometry(width, height)); + + std::string action = kgramstats.randomSentence(rand() % 15 + 5); + Magick::Image textimage(Magick::Geometry(width, height), "transparent"); + Magick::Image shadowimage(Magick::Geometry(width, height), "transparent"); + layoutText(textimage, shadowimage, width, height, action); + image.composite(shadowimage, 0, 0, Magick::OverCompositeOp); + image.composite(textimage, 0, 0, Magick::OverCompositeOp); + + image.magick("jpeg"); + + Magick::Blob outputimg; + image.write(&outputimg); + + delete[] buffer; + + std::cout << "Generated image." << std::endl << "Tweeting..." << std::endl; + + long media_id; + twitter::response resp = client.uploadMedia("image/jpeg", (const char*) outputimg.data(), outputimg.length(), media_id); + if (resp != twitter::response::ok) + { + std::cout << "Twitter error while uploading image: " << resp << std::endl; + + break; + } + + twitter::tweet tw; + resp = client.updateStatus("", tw, twitter::tweet(), {media_id}); + if (resp != twitter::response::ok) + { + std::cout << "Twitter error while tweeting: " << resp << std::endl; + + break; + } + + std::cout << "Done!" << std::endl << "Waiting..." << std::endl << std::endl; + + break; + } + } + + std::this_thread::sleep_for(std::chrono::hours(1)); + } + + return 0; +} -- cgit 1.4.1